Le Duy Khuong

Chuỗi: seo-posthog · Phần 2

Năng suất & công cụ dev

Session Replay — Xem người dùng thực

DOM replay, privacy, frustration signals

2026-03-206 phút đọcVI

Session Replay — Watching Real Users

Mở đầu

Heatmaps cho biết "users click ở đâu." Funnel analysis cho biết "bao nhiêu % users hoàn thành." Nhưng không gì thay thế việc xem thực tế user dùng site — mouse di chuyển thế nào, scroll đến đâu, bị stuck ở đâu, bỏ đi khi nào.

Session Replay là PostHog's killer feature. Nó record lại toàn bộ user session — mouse movement, scrolls, clicks, page navigation — dưới dạng video anh có thể xem lại. Không phải screen recording (video nặng) — PostHog dùng DOM snapshot + mutations (nhẹ, efficient).

Mục tiêu: Hiểu cách Session Replay hoạt động, privacy controls trên leduykhuong.com, và cách extract UX insights.


Cách hoạt động — DOM Replay, không phải Video

Session Replay KHÔNG record video pixels. Nó:

  1. Snapshot DOM lúc page load — capture full HTML tree
  2. Record mutations — DOM changes (class toggle, text update, element add/remove)
  3. Record events — mouse position (x, y), clicks, scrolls, page navigations
  4. Gửi data lên PostHog server
  5. Replay bằng cách rebuild DOM + replay events → trông như video nhưng thực chất là DOM reconstruction

Ưu điểm:

  • Data nhỏ hơn video ~100x (text-based, không phải pixels)
  • Có thể inspect elements trong replay (click vào element → xem selector)
  • Không capture nội dung bị masked (privacy)

Configuration trên leduykhuong.com

// components/analytics/PostHogProvider.tsx
posthog.init(POSTHOG_KEY, {
  // ...
  session_recording: {
    maskAllInputs: true,    // Mask all <input> elements
  },
});

Privacy Controls

SettingGiá trịÝ nghĩa
maskAllInputstrueChe tất cả form inputs (search, etc.)
maskTextSelector(default)Không mask text mặc định
blockSelector(not set)Không block element nào

maskAllInputs: true — Trong session replay, mọi <input> value hiển thị là ***. User gõ search query → replay chỉ thấy ***. Đây là privacy-first approach:

  • Không record passwords
  • Không record search queries (có thể chứa personal info)
  • Không record form data

Nếu cần mask thêm:

session_recording: {
  maskAllInputs: true,
  maskTextSelector: ".sensitive-text", // Mask text content of specific elements
  blockSelector: ".no-record",         // Completely exclude elements from recording
}

Xem Session Replay

PostHog Dashboard → Session Replay:

Replay Player Interface

┌─────────────────────────────────────────────────┐
│  ▶ Play  ⏸ Pause  1x Speed  [Timeline]         │
│                                                   │
│  ┌───────────────────────────────────────────┐   │
│  │                                             │   │
│  │   [Reconstructed page]                      │   │
│  │   Mouse cursor: ⊕ (moves in real-time)      │   │
│  │   Clicks: highlighted with pulse animation   │   │
│  │   Scrolls: page scrolls                      │   │
│  │                                             │   │
│  └───────────────────────────────────────────┘   │
│                                                   │
│  Events timeline: ●──●───●─●──────●──●          │
│                   pv  click  scroll   pv          │
│                                                   │
│  Duration: 3m 45s  |  Pages: 4  |  Events: 23   │
└─────────────────────────────────────────────────┘

Filter Sessions

Không cần xem tất cả — filter for interesting sessions:

FilterKhi nào dùng
Duration > 2 minLong sessions (engaged users)
Duration < 15 secBounce sessions (frustrated users)
Visited URL contains /blog/Blog readers only
Event = search_usedUsers who searched
Frustration signalsRage clicks, U-turns

Frustration Signals

PostHog auto-detect frustration patterns:

  • Rage clicks — User click cùng chỗ 3+ lần nhanh (element không respond)
  • Dead clicks — Click vào nơi không có interactive element
  • U-turns — Navigate forward rồi immediately back
  • Thrashing — Mouse di chuyển nhanh, không mục đích

UX Insights — Cách extract giá trị

Pattern 1: Navigation Confusion

Observation: 5/10 users click vào tag pill rồi immediately back. Insight: Tag page content không match expectation. Users expect more articles, thấy ít hoặc không đúng. Action: Improve tag pages hoặc set minimum post count trước khi hiển thị tag.

Pattern 2: Search Discovery

Observation: Users scroll lên xuống blog listing 3-4 lần trước khi tìm thấy bài. Insight: Blog listing thiếu search functionality visible. Users cần tìm kiếm nhưng không thấy search bar. Action: Make search more prominent (sticky header, search icon).

Pattern 3: Reading Depth

Observation: Users đọc 70% bài rồi dừng tại code example. Insight: Code example quá dài hoặc phức tạp → users skip hoặc abandon. Action: Simplify code examples, add explanations, break into smaller chunks.

Pattern 4: Mobile UX

Observation: Mobile users struggle tap TOC links (too close together). Insight: Touch targets quá nhỏ. Action: Increase spacing giữa TOC items trên mobile.


Session Replay + Events — Combining Context

PostHog cho phép filter replay theo events:

Show me sessions where:
  - User triggered "search_used"
  - AND visited > 3 pages
  - AND duration > 2 minutes

→ Xem HOW engaged users dùng search → learn patterns để optimize cho other users.

Hoặc:

Show me sessions where:
  - User visited /vi/blog/data-as-product
  - AND bounced (1 page only)

→ Xem WHY users bounce từ bài cụ thể → maybe content doesn't match title? Maybe page load chậm?


Performance Impact

Session Replay thêm ~15-30KB initial load (PostHog SDK) + continuous data gửi lên server. Impact:

MetricWithout PostHogWith PostHog
JS bundle~200KB~230KB
Network requestsNN + recording data
LCP~1.2s~1.3s
MemoryNormal+5-10MB

Mitigation trên leduykhuong.com:

  1. Production onlyAnalyticsProvider gate = không load trên dev
  2. afterInteractive strategy — GA4 load after interactive
  3. PostHog lazy load — PostHog init trong useEffect (sau hydration)

Free Tier Limits

PostHog free tier: 5,000 session recordings/month. Với leduykhuong.com (~500-1000 sessions/month), thoải mái dùng.

Nếu traffic tăng, PostHog cho phép sampling:

posthog.init(POSTHOG_KEY, {
  session_recording: {
    sample_rate: 0.5, // Record 50% of sessions
  },
});

Thực hành

Bài tập 1: Xem session replay

Mở PostHog → Session Replay:

  • Xem 3 sessions ngẫu nhiên
  • Ghi nhận: users navigate thế nào? Họ click gì? Bỏ đi ở đâu?

Bài tập 2: Filter frustrated users

Filter: sessions với rage clicks hoặc duration < 15s:

  • Xem 2-3 sessions
  • Câu hỏi: Frustration xảy ra ở đâu? Element nào gây confusion?

Bài tập 3: Blog reading pattern

Filter: sessions visiting /blog/* với duration > 2 min:

  • Users scroll đến đâu?
  • Có dừng lại đọc code examples không?
  • TOC được dùng không?

Tóm tắt

  • Session Replay = DOM reconstruction, không phải video recording → nhẹ, privacy-friendly
  • maskAllInputs: true — Che tất cả form inputs trong replay
  • Frustration signals — PostHog auto-detect rage clicks, dead clicks, U-turns
  • UX insights — Xem actual user behavior → discover navigation confusion, reading patterns, mobile issues
  • Events + Replay — Filter sessions theo events để xem behavior in context
  • Free tier — 5,000 recordings/month, thừa cho personal blog

Bài tiếp theo

Bài 15: Funnels & User Paths — Vẽ user journey: Blog landing → Click bài → Đọc xong → Click bài tiếp. Bao nhiêu % users hoàn thành mỗi step?

LDK

Le Duy Khuong

AI Transformation & Digital Strategy. Writing about agentic systems, engineering leadership, and building in public.