Implement product analytics for understanding user behavior, tracking key metrics, and making data-driven decisions. Covers event tracking, user identification, cohort analysis, and integration with analytics platforms like PostHog, Mixpanel, or custom solutions.
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
npx agent-skills-cli listSkill Instructions
name: saas-analytics version: 1.0.0 category: analytics triggers:
- analytics
- tracking
- metrics
- events
- usage tracking
- product analytics
- user analytics
- posthog
- mixpanel
- amplitude
- segment specialist: "@analyst" stack_aware: true complexity: intermediate estimated_tokens: 3600 dependencies: []
SaaS Product Analytics
Capability
Implement product analytics for understanding user behavior, tracking key metrics, and making data-driven decisions. Covers event tracking, user identification, cohort analysis, and integration with analytics platforms like PostHog, Mixpanel, or custom solutions.
Use Cases
- Track user actions and feature usage
- Measure activation and retention metrics
- Identify power users and at-risk accounts
- A/B test experiments tracking
- Funnel analysis and conversion tracking
- Custom dashboard metrics
Patterns
Analytics Service Abstraction
When to use: Provider-agnostic analytics with consistent API
Implementation: Abstract analytics behind unified interface for easy provider switching.
// Analytics interface
interface AnalyticsService {
identify(userId: string, traits: Record<string, unknown>): Promise<void>;
track(userId: string, event: string, properties?: Record<string, unknown>): Promise<void>;
page(userId: string, name: string, properties?: Record<string, unknown>): Promise<void>;
group(userId: string, groupId: string, traits: Record<string, unknown>): Promise<void>;
}
// PostHog implementation
class PostHogAnalytics implements AnalyticsService {
private client: PostHog;
constructor(apiKey: string, host?: string) {
this.client = new PostHog(apiKey, { host: host || 'https://app.posthog.com' });
}
async identify(userId: string, traits: Record<string, unknown>) {
this.client.identify({
distinctId: userId,
properties: traits
});
}
async track(userId: string, event: string, properties?: Record<string, unknown>) {
this.client.capture({
distinctId: userId,
event,
properties: {
...properties,
timestamp: new Date().toISOString()
}
});
}
async page(userId: string, name: string, properties?: Record<string, unknown>) {
this.client.capture({
distinctId: userId,
event: '$pageview',
properties: { $current_url: name, ...properties }
});
}
async group(userId: string, groupId: string, traits: Record<string, unknown>) {
this.client.groupIdentify({
groupType: 'organization',
groupKey: groupId,
properties: traits
});
this.client.capture({
distinctId: userId,
event: '$groupidentify',
properties: { $group_type: 'organization', $group_key: groupId }
});
}
shutdown() {
this.client.shutdown();
}
}
// Usage
const analytics = new PostHogAnalytics(process.env.POSTHOG_API_KEY);
// Identify user
await analytics.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan,
createdAt: user.createdAt
});
// Track event
await analytics.track(user.id, 'project_created', {
projectId: project.id,
projectType: project.type
});
Event Schema & Naming Conventions
When to use: Maintain consistent, queryable analytics data
Implementation: Define standard event schema with naming conventions.
// Event naming convention: object_action
// Examples: project_created, user_invited, feature_used
// Standard event properties
interface BaseEventProperties {
timestamp: string;
sessionId?: string;
source?: 'web' | 'api' | 'mobile';
version?: string;
}
// Event catalog with type safety
const EVENTS = {
// Auth events
user_signed_up: (props: { method: 'email' | 'google' | 'github' }) => props,
user_logged_in: (props: { method: string }) => props,
user_logged_out: () => ({}),
// Onboarding events
onboarding_started: () => ({}),
onboarding_step_completed: (props: { step: string; duration: number }) => props,
onboarding_completed: (props: { totalDuration: number }) => props,
onboarding_skipped: (props: { lastStep: string }) => props,
// Core feature events
project_created: (props: { projectId: string; template?: string }) => props,
project_deleted: (props: { projectId: string }) => props,
feature_used: (props: { feature: string; context?: string }) => props,
// Team events
team_member_invited: (props: { role: string }) => props,
team_member_joined: (props: { inviteId: string }) => props,
// Billing events
plan_viewed: (props: { currentPlan: string }) => props,
plan_selected: (props: { plan: string; interval: 'monthly' | 'yearly' }) => props,
checkout_started: (props: { plan: string }) => props,
subscription_created: (props: { plan: string; mrr: number }) => props,
subscription_cancelled: (props: { plan: string; reason?: string }) => props
} as const;
// Type-safe track function
async function trackEvent<E extends keyof typeof EVENTS>(
userId: string,
event: E,
properties: ReturnType<typeof EVENTS[E]>
) {
await analytics.track(userId, event, {
...properties,
timestamp: new Date().toISOString()
});
}
// Usage with full type safety
await trackEvent(user.id, 'project_created', {
projectId: project.id,
template: 'starter'
});
Server-Side Tracking
When to use: Track events from API routes and server actions
Implementation: Middleware and helpers for consistent server tracking.
// Analytics context in request
declare global {
namespace Express {
interface Request {
analytics: {
userId: string;
sessionId: string;
track: (event: string, properties?: Record<string, unknown>) => Promise<void>;
};
}
}
}
// Analytics middleware
async function analyticsMiddleware(req: Request, res: Response, next: NextFunction) {
const userId = req.session?.userId;
const sessionId = req.cookies?.sessionId || generateSessionId();
req.analytics = {
userId,
sessionId,
track: async (event, properties) => {
if (!userId) return; // Don't track anonymous
await analytics.track(userId, event, {
...properties,
sessionId,
path: req.path,
method: req.method
});
}
};
// Track page view for GET requests
if (req.method === 'GET' && userId) {
await analytics.page(userId, req.path);
}
next();
}
// Usage in API route
app.post('/api/projects', async (req, res) => {
const project = await createProject(req.body);
await req.analytics.track('project_created', {
projectId: project.id,
projectType: project.type
});
res.json(project);
});
Client-Side Tracking
When to use: Track UI interactions and page views
Implementation: React hooks and components for frontend tracking.
// Analytics context provider
'use client';
import { createContext, useContext, useEffect } from 'react';
import posthog from 'posthog-js';
const AnalyticsContext = createContext<{
track: (event: string, properties?: Record<string, unknown>) => void;
identify: (userId: string, traits: Record<string, unknown>) => void;
} | null>(null);
export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST
});
}, []);
const track = (event: string, properties?: Record<string, unknown>) => {
posthog.capture(event, properties);
};
const identify = (userId: string, traits: Record<string, unknown>) => {
posthog.identify(userId, traits);
};
return (
<AnalyticsContext.Provider value={{ track, identify }}>
{children}
</AnalyticsContext.Provider>
);
}
export function useAnalytics() {
const context = useContext(AnalyticsContext);
if (!context) throw new Error('useAnalytics must be used within AnalyticsProvider');
return context;
}
// Usage in component
function CreateProjectButton() {
const { track } = useAnalytics();
const handleClick = async () => {
track('create_project_clicked', { location: 'dashboard' });
// ... create project
track('project_created', { projectId: newProject.id });
};
return <button onClick={handleClick}>Create Project</button>;
}
// Auto-track page views
function PageViewTracker() {
const pathname = usePathname();
const { track } = useAnalytics();
useEffect(() => {
track('$pageview', { path: pathname });
}, [pathname, track]);
return null;
}
Key Metrics Tracking
When to use: Track SaaS health metrics
Implementation: Calculate and store key metrics for dashboards.
// Daily metrics aggregation job
async function calculateDailyMetrics(date: Date) {
const startOfDay = new Date(date.setHours(0, 0, 0, 0));
const endOfDay = new Date(date.setHours(23, 59, 59, 999));
// Active users (logged in today)
const dau = await db.query.sessions.count({
where: and(
gte(sessions.createdAt, startOfDay),
lte(sessions.createdAt, endOfDay)
)
});
// New signups
const newUsers = await db.query.users.count({
where: and(
gte(users.createdAt, startOfDay),
lte(users.createdAt, endOfDay)
)
});
// Activated users (completed key action)
const activatedUsers = await db.query.users.count({
where: and(
gte(users.activatedAt, startOfDay),
lte(users.activatedAt, endOfDay)
)
});
// MRR (Monthly Recurring Revenue)
const mrr = await calculateMRR();
// Store metrics
await db.insert(dailyMetrics).values({
date: startOfDay,
dau,
newUsers,
activatedUsers,
activationRate: newUsers > 0 ? activatedUsers / newUsers : 0,
mrr,
churnRate: await calculateChurnRate(startOfDay)
});
}
// Key metrics dashboard API
app.get('/api/admin/metrics', async (req, res) => {
const { period = '30d' } = req.query;
const days = parseInt(period) || 30;
const metrics = await db.query.dailyMetrics.findMany({
where: gte(dailyMetrics.date, subDays(new Date(), days)),
orderBy: asc(dailyMetrics.date)
});
// Calculate trends
const currentPeriod = metrics.slice(-days / 2);
const previousPeriod = metrics.slice(0, days / 2);
res.json({
metrics,
summary: {
totalDAU: sum(currentPeriod, 'dau'),
dauTrend: calculateTrend(previousPeriod, currentPeriod, 'dau'),
currentMRR: metrics[metrics.length - 1]?.mrr || 0,
mrrGrowth: calculateGrowth(previousPeriod, currentPeriod, 'mrr'),
avgActivationRate: average(currentPeriod, 'activationRate')
}
});
});
Stack Implementations
{{stack.services.analytics}} Integration
PostHog (Recommended for product analytics):
import { PostHog } from 'posthog-node';
const posthog = new PostHog(process.env.POSTHOG_API_KEY);
Mixpanel:
import Mixpanel from 'mixpanel';
const mixpanel = Mixpanel.init(process.env.MIXPANEL_TOKEN);
Segment (for multi-destination):
import Analytics from '@segment/analytics-node';
const analytics = new Analytics({ writeKey: process.env.SEGMENT_WRITE_KEY });
Quality Checklist
- Analytics abstracted from provider
- Consistent event naming convention
- User identification on signup/login
- Organization/group association for B2B
- Server-side tracking for critical events
- Client-side tracking for UI interactions
- Key metrics calculated and stored daily
- Event schema documented for team
- PII handling compliant with privacy laws
- Analytics disabled in development/test
Anti-Patterns
Tracking Everything
// WRONG: Noise drowns out signal
track('button_hovered');
track('scroll_position_changed');
track('mouse_moved');
// RIGHT: Track meaningful actions
track('feature_used', { feature: 'export', format: 'csv' });
No User Context
// WRONG: Can't attribute to user
analytics.track('project_created');
// RIGHT: Always include user
analytics.track(userId, 'project_created', { projectId });
Inconsistent Naming
// WRONG: Inconsistent patterns
track('UserCreatedProject');
track('project-deleted');
track('BILLING_UPGRADED');
// RIGHT: Consistent object_action format
track('project_created');
track('project_deleted');
track('subscription_upgraded');
More by TheWayWithin
View allImplement production-ready authentication for SaaS applications β email/password, OAuth social login (Google, GitHub), session management, JWT handling, password reset, email verification, and magic links. Use when building signup, sign-in, login, signup, password reset, or any auth-related feature.
Implement production-ready authentication for SaaS applications including email/password, OAuth social login, session management, and security best practices. This skill covers the complete auth lifecycle from signup through password recovery.
Implement subscription lifecycle management, plan enforcement, usage tracking, and billing operations. Covers trial periods, plan changes, quota enforcement, and subscription status synchronization with payment providers.
Implement production-ready payment processing with Stripe including one-time payments, recurring subscriptions, customer portal, metered billing, and webhook handling. This skill covers the complete billing lifecycle from checkout through subscription management.
