As I continue building my AI Product Description Generator, I realized I needed to understand how people would actually use the tool. After researching different options, I decided to add PostHog analytics to track user behavior and make data-driven improvements.
Why PostHog?
When choosing an analytics solution, several factors made PostHog stand out:
- Open Source: The entire platform is open source, which aligns with my development values
- Developer-First: Built specifically with developers in mind
- Next.js Integration: Clean documentation and easy setup with my tech stack
- Session Recordings: The ability to see how users interact with the UI
- Self-hostable: Option to self-host in the future if needed
Setting Up PostHog in Next.js
The setup process was straightforward. Here's how I added PostHog to my project:
- First, install the PostHog package:
npm install --save posthog-js
# or
yarn add posthog-js
# or
pnpm add posthog-js
- Add your environment variables to
.env.local
:
NEXT_PUBLIC_POSTHOG_KEY=your-project-key
NEXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
These variables need to start with NEXT_PUBLIC_
to be accessible on the client side.
- For my Next.js app using the App Router, I created two key files:
First, a providers file for PostHog initialization:
// app/providers.tsx
'use client'
import posthog from 'posthog-js'
import { PostHogProvider } from 'posthog-js/react'
import { useEffect } from 'react'
const PostHogPageView = dynamic(() => import('./PostHogPageView'), {
ssr: false,
})
export function PHProvider({
children,
}: {
children: React.ReactNode
}) {
useEffect(() => {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: "/ingest",
ui_host: "https://eu.posthog.com",
person_profiles: 'identified_only',
capture_pageview: false,
capture_pageleave: true
})
}, []);
return (
<PostHogProvider client={posthog}>
<PostHogPageView/>
{children}
</PostHogProvider>
)
}
Note: We use dynamic import for PostHogPageView because it contains the useSearchParams hook, which would otherwise force the entire app into client-side rendering.
Then, a component to handle page view tracking (since Next.js is a single-page app):
// app/PostHogPageView.tsx
'use client'
import { usePathname, useSearchParams } from "next/navigation"
import { useEffect } from "react"
import { usePostHog } from 'posthog-js/react'
export default function PostHogPageView(): null {
const pathname = usePathname()
const searchParams = useSearchParams()
const posthog = usePostHog()
useEffect(() => {
if (pathname && posthog) {
let url = window.origin + pathname
if (searchParams.toString()) {
url = url + `?${searchParams.toString()}`
}
posthog.capture(
'$pageview',
{
'$current_url': url,
}
)
}
}, [pathname, searchParams, posthog])
return null
}
Finally, I integrated these components in my root layout:
// app/layout.tsx
import './globals.css'
import { PHProvider } from './providers'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<PHProvider>
<body>
<PostHogPageView />
{children}
</body>
</PHProvider>
</html>
)
}
Setting Up a Reverse Proxy
To improve privacy and avoid ad-blockers, I also set up a reverse proxy using Next.js rewrites. Here's how:
- First, I added the proxy configuration to
next.config.js
:
// next.config.js
const nextConfig = {
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*",
},
{
source: "/ingest/decide",
destination: "https://us.i.posthog.com/decide",
},
];
},
// Required to support PostHog trailing slash API requests
skipTrailingSlashRedirect: true,
}
module.exports = nextConfig
- Then, I updated the PostHog initialization to use the proxy:
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: "/ingest",
ui_host: 'https://eu.posthog.com' // Adjust if you're using EU cloud
// ... other options
})
Note: If you're using PostHog's EU cloud (like I am), replace us
with eu
in all domains in the next.config.js
file.
What I'm Planning to Track
I'm starting with basic events to understand user behavior:
- Core User Actions:
// Track when users attempt to generate descriptions
function handleGenerate() {
posthog.capture('generate_description', {
productType: product.type,
inputLength: product.input.length
})
}
// Track successful generations
function onGenerationSuccess(result) {
posthog.capture('generation_success', {
responseLength: result.length,
timeToGenerate: performance.now() - startTime
})
}
// Track errors
function onGenerationError(error) {
posthog.capture('generation_error', {
errorType: error.type,
errorMessage: error.message
})
}
- User Flow Events:
// Track form interactions
function trackFormInteraction(fieldName: string, action: string) {
posthog.capture('form_interaction', {
field: fieldName,
action: action // focus, blur, change, etc.
})
}
Questions I Want to Answer
By implementing analytics, I'm hoping to understand:
- User Behavior
- How many descriptions do users typically generate?
- What types of products are they describing?
Where do users get stuck in the process?
Performance Metrics
How long do generations typically take?
Are there common error patterns?
What's the success rate of generations?
Usage Patterns
What times are most active?
Which features are used most?
Do users return for multiple sessions?
Next Steps
Now that the basic setup is complete, my next steps are:
- Create Funnels
- Track the complete user journey
- Identify drop-off points
Measure conversion rates
Set Up A/B Testing
Test different UI layouts
Experiment with form fields
Try various generation prompts
Monitor Performance
Track load times
Measure API response times
Identify bottlenecks
Learning in Public
This is just the beginning of my analytics journey. I'll be sharing what I learn as I gather real user data and make improvements based on these insights.
Some questions I'm curious about:
- What metrics do you track in your projects?
- How do you balance privacy with data collection?
- What analytics insights have surprised you the most?
I'd love to hear about your experiences with analytics and any suggestions for what I should be tracking. Drop your thoughts in the comments!
Keep following my journey:
- Twitter: ereiswebdev
- GitHub: Ereis
- Live Demo: DescribeWise
Let's learn and build together! 🚀
Top comments (0)