الهدف
بعد هالدرس تقدر تنصّب PostHog في Next.js بشكل صح — client + server SDK، reverse proxy شغّال، وأول $pageview event يوصل لـ dashboard بدون ما ad-blocker يوقفه.
ليش هذا الحين؟
كل مرة تبدأ مشروع وتقول "بسوّي الـ analytics بعدين" — تخسر data من أول يوم. المشكلة مو بس التأجيل، المشكلة إنك لو نصّبت PostHog غلط (direct to posthog.com بدون proxy)، أكثر من 40% من events مستخدميك تنحجب من ad-blockers. تبني product على بيانات ناقصة = قرارات غلط.
PostHog هو الـ all-in-one analytics stack اللي يغني عن Mixpanel + Hotjar + LaunchDarkly + FullStory — بسعر مناسب و data عندك مو عند غيرك.
الفكرة
PostHog يشتغل بطريقتين: client-side عبر posthog-js (browser)، وserver-side عبر posthog-node (API routes, server components). الاثنين لازم يشتغلون.
المشكلة الكبيرة: لو دزيت events مباشرة لـ https://us.i.posthog.com، الـ browser يشوف الـ hostname ويحجبه. الحل هو reverse proxy — events تمشي عبر domain موقعك (/ingest/*) و Next.js يحوّلها لـ PostHog. الـ ad-blocker ما يعرف الفرق.
الـ initialization لازم يصير مرة وحدة في app/providers.tsx. كل ما تسوّي posthog.init() أكثر من مرة، تطلع لك duplicate events وبيانات مكسورة.
سوّها
الخطوة 1 — نصّب الـ packages:
npm install posthog-js posthog-node
ترى posthog-node للـ server-side — لا تنسى تنصّبه معاه.
الخطوة 2 — حط الـ env vars:
في .env.local:
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxx
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
الـ key تلاقيه في PostHog → Project Settings → Project API keys. بس خل الـ host يبقى us.i.posthog.com — هذا مو الـ proxy بعد، هذا للـ node SDK على السيرفر.
الخطوة 3 — سوّ الـ reverse proxy:
في next.config.ts، زيد هذا:
const nextConfig = {
skipTrailingSlashRedirect: 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*',
},
{
source: '/ingest/decide',
destination: 'https://us.i.posthog.com/decide',
},
]
},
}
export default nextConfig
ترى هذي أهم خطوة — skipTrailingSlashRedirect: true ما ينذكر في الـ PostHog docs بوضوح، بس هو مطلوب عشان الـ /ingest/decide يشتغل صح.
الخطوة 4 — سوّ الـ PHProvider:
سوّ ملف app/providers.tsx:
'use client'
import posthog from 'posthog-js'
import { PostHogProvider } from 'posthog-js/react'
import { useEffect } from 'react'
export function PHProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: '/ingest',
ui_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
capture_pageview: 'history-change',
capture_pageleave: true,
})
}, [])
return <PostHogProvider client={posthog}>{children}</PostHogProvider>
}
لاحظ api_host: '/ingest' — مو https://us.i.posthog.com. الحين الـ events تمشي عبر proxy موقعك. وui_host منفصل يخلي الـ PostHog toolbar يفتح صح.
المفروض capture_pageview: 'history-change' تكون شغّالة عشان Next.js client-side navigation يتتبّع صح — بدونها تشوف بس أول صفحة.
الخطوة 5 — غلّف الـ layout:
في app/layout.tsx:
import { PHProvider } from './providers'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ar" dir="rtl">
<body>
<PHProvider>{children}</PHProvider>
</body>
</html>
)
}
الـ PHProvider يغلّف كل شيء مرة وحدة — جذي صح.
الخطوة 6 — الـ server-side SDK (لـ API routes):
سوّ lib/posthog-server.ts:
import { PostHog } from 'posthog-node'
export const posthogServer = new PostHog(
process.env.NEXT_PUBLIC_POSTHOG_KEY!,
{ host: process.env.NEXT_PUBLIC_POSTHOG_HOST }
)
استخدمه في أي API route أو server action:
import { posthogServer } from '@/lib/posthog-server'
// بعد ما المستخدم يسجّل
await posthogServer.capture({
distinctId: user.id,
event: 'user_signed_up',
properties: { method: 'magic_link' },
})
لا تستخدم posthog-js في server components أبد — هذا browser-only.
تأكّد
- شغّل
npm run dev - افتح PostHog → Activity (أيقونة الـ activity في الشريط الجانبي)
- افتح موقعك في browser — المفروض تشوف
$pageviewevent يجي خلال ثوان - تأكد إن الـ
api_hostفي الـ network tab هو/ingest/...موus.i.posthog.comمباشرة - جرّب تفعّل uBlock Origin وارجع حمّل الصفحة — لو الـ events للحين توصل، معناها الـ proxy شغّال
لو ما شفت events: شيك على NEXT_PUBLIC_POSTHOG_KEY في .env.local، وتأكد إن الـ rewrites انقرأت (سوّ restart للـ dev server بعد ما تعدّل next.config.ts).
إذا خرب شي
الـ events ما توصل بعد تفعيل uBlock:
الـ proxy مو شغّال. تأكد إن next.config.ts فيه skipTrailingSlashRedirect: true وإن الـ api_host في posthog.init() هو /ingest مو hostname خارجي.
Duplicate events:
تحصل لو عندك posthog.init() في أكثر من مكان — مثلا في providers.tsx وفي صفحة ثانية. الـ init لازم يصير مرة وحدة بس.
$pageview يتسجّل مرة وحدة وما يمشي مع الـ navigation:
capture_pageview إما false (وتتحكم فيه يدوي) أو 'history-change'. بدونها Next.js SPA navigation ما ينسجّل.
posthog is not defined في server component:
posthog-js هو browser-only. استخدم posthog-node في أي كود يشتغل على السيرفر.
الـ PostHog toolbar ما يفتح:
تأكد إن ui_host موجود في posthog.init() ويشير على https://us.i.posthog.com (مو /ingest).
شنو بعد؟
الحين PostHog شغّال و data تجي نظيفة. الدرس الجاي: نصمّم الـ event taxonomy — شنو الـ events اللي تتبّعها، وشلون تسمّيها، وشنو اللي تتجنّب تتبّعه. هذا هو الفرق بين analytics تفيدك و analytics تضيّع وقتك.
النقاشات (0)
سجّل دخول للمشاركة
كون أول واحد يبدأ النقاش.