Adding Analytics to My AI Product Description Generator with PostHog
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! 🚀