Skip to content

Commit 7dd3ef8

Browse files
committed
chore: add a posthog react playground that does nothing
1 parent f061ad9 commit 7dd3ef8

File tree

11 files changed

+428
-0
lines changed

11 files changed

+428
-0
lines changed

playground/react-nextjs/.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Next.js
2+
/.next/
3+
/out/
4+
5+
# production
6+
/build
7+
8+
# misc
9+
.DS_Store
10+
*.pem
11+
12+
# debug
13+
npm-debug.log*
14+
yarn-debug.log*
15+
yarn-error.log*
16+
17+
# local env files
18+
.env*.local
19+
20+
# typescript
21+
*.tsbuildinfo
22+
next-env.d.ts
23+
24+
# pnpm
25+
pnpm-lock.yaml

playground/react-nextjs/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# PostHog React Playground
2+
3+
This is a Next.js playground application that demonstrates the `@posthog/react` SDK.
4+
5+
## Features
6+
7+
- **Cat Gallery**: A grid of cat images from [cataas.com](https://cataas.com)
8+
- **Event Display**: Real-time display of PostHog events in the top-right corner
9+
10+
## Running the Playground
11+
12+
**Important**: This playground is excluded from the main workspace, so you need to install dependencies separately.
13+
14+
1. Navigate to this directory:
15+
16+
```bash
17+
cd playground/react-nextjs
18+
```
19+
20+
2. Install dependencies:
21+
22+
```bash
23+
pnpm install
24+
```
25+
26+
3. Run the development server:
27+
28+
```bash
29+
pnpm dev
30+
```
31+
32+
4. Open [http://localhost:3000](http://localhost:3000) in your browser
33+
34+
5. Scroll down to see the cat gallery come into view and watch the `$element_viewed` event appear in the event display
35+
36+
## How It Works
37+
38+
- The `EventDisplay` component intercepts PostHog events and displays them in real-time
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
'use client'
2+
3+
import { CaptureResult } from 'posthog-js'
4+
import { useState, useEffect } from 'react'
5+
6+
interface EventDisplayProps {
7+
events: CaptureResult[]
8+
}
9+
10+
interface EventWithTimestamp extends CaptureResult {
11+
capturedAt: number
12+
}
13+
14+
export function EventDisplay({ events }: EventDisplayProps) {
15+
const [expandedEvents, setExpandedEvents] = useState<Set<string>>(new Set())
16+
const [eventsWithTimestamp, setEventsWithTimestamp] = useState<EventWithTimestamp[]>([])
17+
const [, setTick] = useState(0)
18+
19+
useEffect(() => {
20+
const newEvents = events.filter((e) => !eventsWithTimestamp.find((existing) => existing.uuid === e.uuid))
21+
if (newEvents.length > 0) {
22+
setEventsWithTimestamp((prev) => [...prev, ...newEvents.map((e) => ({ ...e, capturedAt: Date.now() }))])
23+
}
24+
}, [events, eventsWithTimestamp])
25+
26+
useEffect(() => {
27+
const interval = setInterval(() => setTick((t) => t + 1), 1000)
28+
return () => clearInterval(interval)
29+
}, [])
30+
31+
const toggleExpanded = (uuid: string) => {
32+
setExpandedEvents((prev) => {
33+
const next = new Set(prev)
34+
if (next.has(uuid)) {
35+
next.delete(uuid)
36+
} else {
37+
next.add(uuid)
38+
}
39+
return next
40+
})
41+
}
42+
43+
const getTimeAgo = (timestamp: number) => {
44+
const seconds = Math.floor((Date.now() - timestamp) / 1000)
45+
if (seconds < 60) return `${seconds}s ago`
46+
const minutes = Math.floor(seconds / 60)
47+
if (minutes < 60) return `${minutes}m ago`
48+
const hours = Math.floor(minutes / 60)
49+
return `${hours}h ago`
50+
}
51+
52+
return (
53+
<div
54+
style={{
55+
position: 'fixed',
56+
top: '1rem',
57+
right: '1rem',
58+
width: '320px',
59+
maxHeight: '400px',
60+
overflowY: 'scroll',
61+
backgroundColor: 'white',
62+
border: '2px solid #d1d5db',
63+
borderRadius: '8px',
64+
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
65+
padding: '1rem',
66+
zIndex: 50,
67+
}}
68+
>
69+
<h2 style={{ fontSize: '1.125rem', fontWeight: 'bold', marginBottom: '0.75rem', color: '#1f2937' }}>
70+
PostHog Events
71+
</h2>
72+
{eventsWithTimestamp.length === 0 ? (
73+
<p style={{ color: '#6b7280', fontSize: '0.875rem' }}>No events captured yet...</p>
74+
) : (
75+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
76+
{eventsWithTimestamp.map((event) => (
77+
<div
78+
key={event.uuid}
79+
style={{
80+
backgroundColor: '#f9fafb',
81+
border: '1px solid #e5e7eb',
82+
borderRadius: '4px',
83+
padding: '0.5rem',
84+
fontSize: '0.75rem',
85+
cursor: 'pointer',
86+
}}
87+
onClick={() => toggleExpanded(event.uuid)}
88+
>
89+
<div
90+
style={{
91+
display: 'flex',
92+
justifyContent: 'space-between',
93+
alignItems: 'center',
94+
}}
95+
>
96+
<div style={{ fontWeight: '600', color: '#2563eb' }}>{event.event}</div>
97+
<div style={{ color: '#9ca3af', fontSize: '0.65rem' }}>
98+
{getTimeAgo(event.capturedAt)}
99+
</div>
100+
</div>
101+
{expandedEvents.has(event.uuid) && (
102+
<div
103+
style={{
104+
marginTop: '0.5rem',
105+
paddingTop: '0.5rem',
106+
borderTop: '1px solid #e5e7eb',
107+
}}
108+
>
109+
<pre
110+
style={{
111+
fontSize: '0.65rem',
112+
color: '#374151',
113+
whiteSpace: 'pre-wrap',
114+
wordBreak: 'break-word',
115+
}}
116+
>
117+
{JSON.stringify(event.properties, null, 2)}
118+
</pre>
119+
</div>
120+
)}
121+
</div>
122+
))}
123+
</div>
124+
)}
125+
</div>
126+
)
127+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
* {
2+
box-sizing: border-box;
3+
padding: 0;
4+
margin: 0;
5+
}
6+
7+
html,
8+
body {
9+
max-width: 100vw;
10+
overflow-x: hidden;
11+
}
12+
13+
body {
14+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell',
15+
'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
16+
-webkit-font-smoothing: antialiased;
17+
-moz-osx-font-smoothing: grayscale;
18+
}
19+
20+
a {
21+
color: inherit;
22+
text-decoration: none;
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Metadata } from 'next'
2+
import { PHProvider } from './providers'
3+
import './globals.css'
4+
5+
export const metadata: Metadata = {
6+
title: 'PostHog InView Playground',
7+
description: 'Test PostHog InView component with a cat gallery',
8+
}
9+
10+
export default function RootLayout({
11+
children,
12+
}: Readonly<{
13+
children: React.ReactNode
14+
}>) {
15+
return (
16+
<html lang="en">
17+
<body>
18+
<PHProvider>{children}</PHProvider>
19+
</body>
20+
</html>
21+
)
22+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
'use client'
2+
3+
import Image from 'next/image'
4+
5+
const catImages = Array.from({ length: 120 }, (_, i) => ({
6+
id: i + 1,
7+
url: `https://cataas.com/cat?width=400&height=300&${i + 1}`,
8+
alt: `Cat ${i + 1}`,
9+
}))
10+
11+
export default function Home() {
12+
return (
13+
<main style={{ minHeight: '100vh', padding: '2rem', backgroundColor: '#f5f5f5' }}>
14+
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
15+
<h1
16+
style={{
17+
fontSize: '2.5rem',
18+
fontWeight: 'bold',
19+
marginBottom: '1rem',
20+
color: '#333',
21+
}}
22+
>
23+
Cat Gallery - PostHog React Demo
24+
</h1>
25+
<p style={{ fontSize: '1.125rem', marginBottom: '2rem', color: '#666' }}>
26+
Scroll down to see the cat gallery. When it comes into view, PostHog will track the event and
27+
display it in the top right corner.
28+
</p>
29+
30+
<div style={{ height: '50vh', backgroundColor: '#e0e0e0', marginBottom: '2rem', padding: '2rem' }}>
31+
<h2 style={{ fontSize: '1.5rem', color: '#555' }}>Scroll down to see the gallery...</h2>
32+
</div>
33+
34+
<div
35+
style={{
36+
display: 'grid',
37+
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
38+
gap: '1.5rem',
39+
padding: '2rem',
40+
backgroundColor: 'white',
41+
borderRadius: '8px',
42+
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
43+
}}
44+
>
45+
{catImages.map((cat, index) => (
46+
<div
47+
key={cat.id}
48+
style={{
49+
position: 'relative',
50+
aspectRatio: '4/3',
51+
borderRadius: '8px',
52+
overflow: 'hidden',
53+
backgroundColor: '#ddd',
54+
}}
55+
>
56+
<Image
57+
src={cat.url}
58+
alt={cat.alt}
59+
loading={index < 5 ? 'eager' : 'lazy'}
60+
fill
61+
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
62+
style={{ objectFit: 'cover' }}
63+
/>
64+
</div>
65+
))}
66+
</div>
67+
68+
<div style={{ height: '50vh', marginTop: '2rem' }}>
69+
<p style={{ color: '#666' }}>End of page</p>
70+
</div>
71+
</div>
72+
</main>
73+
)
74+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use client'
2+
3+
import posthog, { CaptureResult } from 'posthog-js'
4+
import { PostHogProvider } from '@posthog/react'
5+
import { useEffect, useState } from 'react'
6+
import { EventDisplay } from './EventDisplay'
7+
8+
let eventListeners: ((event: CaptureResult | null) => void)[] = []
9+
10+
export function addEventListener(callback: (event: CaptureResult | null) => void) {
11+
eventListeners.push(callback)
12+
return () => {
13+
eventListeners = eventListeners.filter((cb) => cb !== callback)
14+
}
15+
}
16+
17+
if (typeof window !== 'undefined') {
18+
posthog.init('phc_test_key_for_playground', {
19+
api_host: 'https://us.i.posthog.com',
20+
person_profiles: 'identified_only',
21+
capture_pageview: 'history_change',
22+
capture_pageleave: true,
23+
before_send: (event) => {
24+
eventListeners.forEach((callback) => callback(event))
25+
console.log('Yo! An event', event?.event, event)
26+
return event
27+
},
28+
})
29+
}
30+
31+
export function PHProvider({ children }: { children: React.ReactNode }) {
32+
const [events, setEvents] = useState<CaptureResult[]>([])
33+
34+
useEffect(() => {
35+
const removeListener = addEventListener((event) => {
36+
if (!event) {
37+
return
38+
}
39+
setEvents((prev) => [event, ...prev].slice(0, 10))
40+
})
41+
42+
posthog.capture('playground_loaded')
43+
44+
return removeListener
45+
}, [])
46+
47+
return (
48+
<PostHogProvider client={posthog}>
49+
<EventDisplay events={events} />
50+
{children}
51+
</PostHogProvider>
52+
)
53+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const nextConfig = {
2+
images: {
3+
remotePatterns: [
4+
{
5+
protocol: 'https',
6+
hostname: 'cataas.com',
7+
},
8+
],
9+
},
10+
}
11+
12+
export default nextConfig

0 commit comments

Comments
 (0)