Skip to content

Commit

Permalink
feat(saas): Implement Analytics with PostHog (#19)
Browse files Browse the repository at this point in the history
## feat: Implement Analytics with PostHog

This pull request integrates PostHog for detailed user and application analytics. 

**Key changes:**

* **PostHog integration:** Added `posthog-js` as a dependency and configured it to capture events and user data.
* **User identification:** Authenticated users are automatically identified in PostHog using their ID and email.
* **Page view tracking:** Page views are automatically tracked, providing insights into user navigation.
* **Rewrites for PostHog API:** Implemented rewrites to handle PostHog API requests efficiently.
* **Admin panel updates:** Added an "Analytics" link to the admin sidebar navigation, directing users to the PostHog dashboard.
* **Environment variables:** Added `NEXT_PUBLIC_POSTHOG_KEY` and `NEXT_PUBLIC_POSTHOG_HOST` to the `.env.example` file for configuration.
* **Provider setup:** Created a `PosthogProvider` component to manage PostHog initialization and user identification.
* **Removed Vercel Analytics:** Replaced the existing Vercel Analytics integration with PostHog.

**Additional notes:**

* This implementation provides a basic foundation for analytics. Further customization and event tracking can be added as needed. 
* The PostHog project ID and dashboard URL should be updated in the `siteUrls` configuration. 

**Testing:**

* Verified that events are being captured correctly in the PostHog dashboard.
* Tested user identification and page view tracking functionality.
  • Loading branch information
alifarooq9 authored Apr 27, 2024
2 parents f0e7f75 + c688b85 commit fd23537
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 35 deletions.
7 changes: 2 additions & 5 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,13 @@
- [x] Get Subscription
- [x] Get Checkout details
- [x] Webhooks
- [ ] Analytics - (Using PostHog)
- [ ] User Analytics
- [x] Analytics - (Using PostHog)
- [ ] Admin Panel
- [ ] Dashboard Page
- [ ] Analytics
- [ ] User Analytics
- [ ] Organisation Analytics
- [ ] Application Analytics
- [ ] Payment Analytics
- [ ] Detail Analytics (Using PostHog)
- [x] User Management
- [ ] Organisation Management
- [x] Feedback Management
Expand All @@ -78,7 +76,6 @@
- [ ] Maintenance Mode
- [ ] Disable Analytics
- [ ] Disable Payments
- [ ] Changelog Management
- [ ] Mobile Responsive all pages

- If you have any other feature ideas, feel free to add it in the roadmap.
Expand Down
43 changes: 19 additions & 24 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions starterkits/saas/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ UPLOADTHING_ID=""
LEMONSQUEEZY_API_KEY=""
LEMONSQUEEZY_STORE_ID=""
LEMONSQUEEZY_WEBHOOK_SECRET=""

# Posthog
NEXT_PUBLIC_POSTHOG_KEY=""
NEXT_PUBLIC_POSTHOG_HOST=""
14 changes: 14 additions & 0 deletions starterkits/saas/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
async rewrites() {
return [
{
source: "/ingest/static/:path*",
destination: "https://us-assets.i.posthog.com/static/:path*",
},
{
source: "/ingest/:path*",
destination: "https://us.i.posthog.com/:path*",
},
];
},
// This is required to support PostHog trailing slash API requests
skipTrailingSlashRedirect: true,
};

export default nextConfig;
2 changes: 1 addition & 1 deletion starterkits/saas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"@tanstack/react-query": "^5.29.2",
"@tanstack/react-table": "^8.16.0",
"@uploadthing/react": "^6.4.4",
"@vercel/analytics": "^1.2.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"cmdk": "^0.2.1",
Expand All @@ -53,6 +52,7 @@
"next-themes": "^0.3.0",
"nodemailer": "^6.9.13",
"postgres": "^3.4.3",
"posthog-js": "^1.130.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.51.3",
Expand Down
2 changes: 0 additions & 2 deletions starterkits/saas/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Toaster } from "@/components/ui/sonner";
import "@/styles/globals.css";
import "@/styles/prism.css";
import { fontHeading, fontSans } from "@/lib/fonts";
import { Analytics } from "@vercel/analytics/react";

export const metadata = {
title: "RapidLaunch - Next.js Boilerplate",
Expand All @@ -27,7 +26,6 @@ export default function RootLayout({
{children}
<Toaster richColors position="top-right" expand />
</Providers>
<Analytics />
</body>
</html>
);
Expand Down
73 changes: 73 additions & 0 deletions starterkits/saas/src/components/posthog-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client";

import { env } from "@/env";
import { useSession } from "next-auth/react";
import { usePathname } from "next/navigation";
import posthog from "posthog-js";
import { PostHogProvider as CSPostHogProvider } from "posthog-js/react";
import { useEffect } from "react";

if (typeof window !== "undefined") {
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: "/ingest",
rate_limiting: {
events_burst_limit: 10,
events_per_second: 5,
},
loaded: (posthog) => {
if (env.NODE_ENV === "development") posthog.debug();
},
});
}

type PostHogProviderProps = {
children: React.ReactNode;
};

export function PosthogProvider({ children }: PostHogProviderProps) {
return (
<>
<CapturePageviewClient captureOnPathChange={true} />
<CSPostHogProvider client={posthog}>
<PosthogAuthWrapper>{children}</PosthogAuthWrapper>
</CSPostHogProvider>
</>
);
}

function PosthogAuthWrapper({ children }: PostHogProviderProps) {
const { data: session, status } = useSession();

useEffect(() => {
if (status === "authenticated") {
posthog.identify(session.user.id, {
email: session.user.email,
name: session.user.name,
});
} else if (status === "unauthenticated") {
posthog.reset();
}
}, [session, status]);

return children;
}

type CapturePageviewClientProps = {
captureOnPathChange?: boolean;
};

export function CapturePageviewClient({
captureOnPathChange = false,
}: CapturePageviewClientProps) {
const pathname = usePathname();

useEffect(() => {
const handleCapturePageview = () => posthog.capture("$pageview");

handleCapturePageview();

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [captureOnPathChange ? pathname : undefined]);

return null;
}
12 changes: 9 additions & 3 deletions starterkits/saas/src/components/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ThemeProvider } from "@/components/theme-provider";
import { PosthogProvider } from "@/components/posthog-provider";
import { SessionProvider } from "next-auth/react";

type ProvidersProps = {
children: React.ReactNode;
Expand All @@ -11,8 +13,12 @@ export function Providers({ children }: ProvidersProps) {
const queryClient = new QueryClient();

return (
<QueryClientProvider client={queryClient}>
<ThemeProvider>{children}</ThemeProvider>
</QueryClientProvider>
<SessionProvider>
<QueryClientProvider client={queryClient}>
<PosthogProvider>
<ThemeProvider>{children}</ThemeProvider>
</PosthogProvider>
</QueryClientProvider>
</SessionProvider>
);
}
6 changes: 6 additions & 0 deletions starterkits/saas/src/config/sidebar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
BarChart4Icon,
BookTextIcon,
CreditCardIcon,
HelpCircleIcon,
Expand Down Expand Up @@ -85,6 +86,11 @@ const navigation: SidebarNavItems[] = [
icon: UsersRoundIcon,
href: siteUrls.admin.users,
},
{
label: "Analytics",
icon: BarChart4Icon,
href: siteUrls.admin.analytics,
},
{
label: "Feedback List",
icon: HelpCircleIcon,
Expand Down
1 change: 1 addition & 0 deletions starterkits/saas/src/config/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const siteUrls = {
users: "/admin/users",
settings: "/admin/settings",
feedbacks: "/admin/feedbacks",
analytics: "https://us.posthog.com/project/12312/dashboard",
},
profile: {
settings: "/profile/settings",
Expand Down
2 changes: 2 additions & 0 deletions starterkits/saas/src/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const env = createEnv({
*/
client: {
// NEXT_PUBLIC_CLIENTVAR: z.string(),
NEXT_PUBLIC_POSTHOG_KEY: z.string(),
},

/**
Expand All @@ -62,6 +63,7 @@ export const env = createEnv({
LEMONSQUEEZY_API_KEY: process.env.LEMONSQUEEZY_API_KEY,
LEMONSQUEEZY_STORE_ID: process.env.LEMONSQUEEZY_STORE_ID,
LEMONSQUEEZY_WEBHOOK_SECRET: process.env.LEMONSQUEEZY_WEBHOOK_SECRET,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
Expand Down

0 comments on commit fd23537

Please sign in to comment.