تخطَّ إلى المحتوى

نصّب PostHog صح من البداية

الدرس 1 من 25 معاينة مجانية

الهدف

بعد هالدرس تقدر تنصّب 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.

تأكّد

  1. شغّل npm run dev
  2. افتح PostHog → Activity (أيقونة الـ activity في الشريط الجانبي)
  3. افتح موقعك في browser — المفروض تشوف $pageview event يجي خلال ثوان
  4. تأكد إن الـ api_host في الـ network tab هو /ingest/... مو us.i.posthog.com مباشرة
  5. جرّب تفعّل 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 تضيّع وقتك.

محتاج مساعدة؟ راسلنا على mj@shakesbeard.net