Skip to content

Commit 5137440

Browse files
authored
feat: add a redux/kea state and action logger to posthog-js (#2294)
1 parent 5bc82b8 commit 5137440

31 files changed

+7407
-408
lines changed

.changeset/shaky-shrimps-sit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'posthog-js': minor
3+
---
4+
5+
feat: add a redux logging middleware to posthog-js

packages/browser/playground/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
A collection of examples for showing how to use PostHog's JS SDK
2+
3+
Or for testing features of it
4+
5+
Inside each folder you need to run `pnpm i --ignore-workspace` to install dependencies
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Dependencies
2+
node_modules/
3+
npm-debug.log*
4+
yarn-debug.log*
5+
yarn-error.log*
6+
pnpm-debug.log*
7+
.pnpm-debug.log*
8+
9+
# Next.js
10+
.next/
11+
out/
12+
build/
13+
dist/
14+
15+
# Production builds
16+
*.tsbuildinfo
17+
next-env.d.ts
18+
19+
# Runtime data
20+
pids
21+
*.pid
22+
*.seed
23+
*.pid.lock
24+
25+
# Coverage directory used by tools like istanbul
26+
coverage/
27+
*.lcov
28+
29+
# nyc test coverage
30+
.nyc_output
31+
32+
# Logs
33+
logs
34+
*.log
35+
36+
# Runtime data
37+
pids
38+
*.pid
39+
*.seed
40+
*.pid.lock
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional stylelint cache
49+
.stylelintcache
50+
51+
# Microbundle cache
52+
.rpt2_cache/
53+
.rts2_cache_cjs/
54+
.rts2_cache_es/
55+
.rts2_cache_umd/
56+
57+
# Optional REPL history
58+
.node_repl_history
59+
60+
# Output of 'npm pack'
61+
*.tgz
62+
63+
# Yarn Integrity file
64+
.yarn-integrity
65+
66+
# dotenv environment variable files
67+
.env
68+
.env.development.local
69+
.env.test.local
70+
.env.production.local
71+
.env.local
72+
73+
# parcel-bundler cache (https://parceljs.org/)
74+
.cache
75+
.parcel-cache
76+
77+
# Stores VSCode versions used for testing VSCode extensions
78+
.vscode-test
79+
80+
# yarn v2
81+
.yarn/cache
82+
.yarn/unplugged
83+
.yarn/build-state.yml
84+
.yarn/install-state.gz
85+
.pnp.*
86+
87+
# IDEs and editors
88+
.idea/
89+
.vscode/
90+
*.swp
91+
*.swo
92+
*~
93+
94+
# OS generated files
95+
.DS_Store
96+
.DS_Store?
97+
._*
98+
.Spotlight-V100
99+
.Trashes
100+
ehthumbs.db
101+
Thumbs.db
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"trailingComma": "es5",
3+
"tabWidth": 4,
4+
"semi": false,
5+
"singleQuote": true,
6+
"printWidth": 120
7+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# PostHog State Management Logging Examples
2+
3+
This is a comparison of PostHog logging integrations with different state management libraries using a todo list application built with Next.js.
4+
5+
## Examples Included
6+
7+
- **Redux**: Uses Redux Toolkit with `posthogReduxLogger` middleware
8+
- **Kea**: Uses Kea with `posthogKeaLogger` plugin (cleaner API)
9+
10+
## To run it
11+
12+
```bash
13+
pnpm i && pnpm dev
14+
```
15+
16+
Then visit:
17+
18+
- **Home**: http://localhost:3000/ - Overview and links to examples
19+
- **Redux**: http://localhost:3000/redux - Redux Toolkit + PostHog logging
20+
- **Kea**: http://localhost:3000/kea - Kea + PostHog logging
21+
22+
## Features Demonstrated
23+
24+
- Action and state logging with PostHog
25+
- Rate limiting to prevent log flooding
26+
- State diffing (only log changed values)
27+
- Action/state masking for sensitive data
28+
- Performance monitoring (slow action detection)
29+
- Different API styles (Redux-specific vs generic)
30+
31+
Apologies to the future traveller, the example todo app is entirely AI slop
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
outputFileTracingRoot: process.cwd(),
4+
}
5+
6+
module.exports = nextConfig
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "redux-todo-list",
3+
"version": "1.0.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"format": "prettier --write 'src/**/*.{ts,tsx}'"
10+
},
11+
"devDependencies": {
12+
"@types/node": "^20.19.9",
13+
"@types/react": "^18.2.0",
14+
"@types/react-dom": "^18.2.0",
15+
"@typescript-eslint/eslint-plugin": "^8.42.0",
16+
"@typescript-eslint/parser": "^8.42.0",
17+
"eslint": "^9.35.0",
18+
"eslint-config-next": "^15.5.2",
19+
"eslint-config-prettier": "^10.1.8",
20+
"eslint-plugin-prettier": "^5.5.4",
21+
"prettier": "^3.6.2",
22+
"typescript": "^5.8.2"
23+
},
24+
"dependencies": {
25+
"@reduxjs/toolkit": "^2.0.0",
26+
"kea": "^3.1.7",
27+
"kea-typegen": "^3.5.0",
28+
"next": "^15.5.2",
29+
"posthog-js": "file:../../dist",
30+
"react": "^18.2.0",
31+
"react-dom": "^18.2.0",
32+
"react-redux": "^9.0.0",
33+
"redux": "^5.0.0"
34+
}
35+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Provider } from 'react-redux'
2+
import { store } from '../src/store'
3+
import type { AppProps } from 'next/app'
4+
import '../styles/globals.css'
5+
import { useRouter } from 'next/router'
6+
7+
export default function App({ Component, pageProps }: AppProps) {
8+
const router = useRouter()
9+
10+
// Only use Redux Provider for Redux pages
11+
if (router.pathname === '/redux') {
12+
return (
13+
<Provider store={store}>
14+
<Component {...pageProps} />
15+
</Provider>
16+
)
17+
}
18+
19+
// For home page, Kea page, and other pages, don't use Redux Provider
20+
return <Component {...pageProps} />
21+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import Head from 'next/head'
2+
3+
export default function Home() {
4+
return (
5+
<>
6+
<Head>
7+
<title>PostHog State Management Examples</title>
8+
<meta name="description" content="Compare PostHog logging with Redux vs Kea" />
9+
<meta name="viewport" content="width=device-width, initial-scale=1" />
10+
<link rel="icon" href="/favicon.ico" />
11+
</Head>
12+
<div className="container">
13+
<h1>PostHog State Management Examples</h1>
14+
<p style={{ textAlign: 'center', color: '#666', marginBottom: '2rem' }}>
15+
Compare PostHog logging with different state management libraries
16+
</p>
17+
18+
<div style={{ display: 'flex', gap: '20px', justifyContent: 'center', flexWrap: 'wrap' }}>
19+
<div className="example-card">
20+
<h3>Redux Example</h3>
21+
<p>
22+
Todo list with Redux Toolkit and <code>posthogReduxLogger</code>
23+
</p>
24+
<ul style={{ textAlign: 'left', margin: '1rem 0' }}>
25+
<li>Redux Toolkit store</li>
26+
<li>Middleware integration</li>
27+
<li>Action/state masking</li>
28+
<li>Rate limiting</li>
29+
<li>Performance monitoring</li>
30+
</ul>
31+
<a href="/redux" className="example-button">
32+
Try Redux Version →
33+
</a>
34+
</div>
35+
36+
<div className="example-card">
37+
<h3>Kea Example</h3>
38+
<p>
39+
Todo list with Kea and <code>posthogKeaLogger</code>
40+
</p>
41+
<ul style={{ textAlign: 'left', margin: '1rem 0' }}>
42+
<li>Kea logic stores</li>
43+
<li>Plugin integration</li>
44+
<li>Cleaner API (maskAction/maskState)</li>
45+
<li>Same rate limiting & features</li>
46+
<li>Performance monitoring</li>
47+
</ul>
48+
<a href="/kea" className="example-button">
49+
Try Kea Version →
50+
</a>
51+
</div>
52+
</div>
53+
54+
<div
55+
style={{
56+
marginTop: '3rem',
57+
padding: '1.5rem',
58+
background: '#f8f9fa',
59+
borderRadius: '8px',
60+
border: '1px solid #e9ecef',
61+
}}
62+
>
63+
<h4 style={{ marginBottom: '1rem', color: '#333' }}>🧑‍💻 Developer Notes</h4>
64+
<ul style={{ textAlign: 'left', color: '#666' }}>
65+
<li>
66+
<strong>Console Logging:</strong> Open browser dev tools to see PostHog action logging
67+
</li>
68+
<li>
69+
<strong>API Comparison:</strong> Redux uses <code>maskReduxAction</code>/
70+
<code>maskReduxState</code>
71+
</li>
72+
<li>
73+
<strong>Kea Cleaner API:</strong> Kea uses <code>maskAction</code>/<code>maskState</code>
74+
</li>
75+
<li>
76+
<strong>Same Features:</strong> Both support rate limiting, state diffing, and performance
77+
monitoring
78+
</li>
79+
<li>
80+
<strong>Integration:</strong> Kea logger reuses Redux logger internally via middleware
81+
injection
82+
</li>
83+
</ul>
84+
</div>
85+
</div>
86+
</>
87+
)
88+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Head from 'next/head'
2+
import { useEffect, useState } from 'react'
3+
import dynamic from 'next/dynamic'
4+
5+
// Dynamically import Kea components to avoid SSR issues
6+
const TodoInput = dynamic(() => import('../src/components/kea/TodoInput'), { ssr: false })
7+
const TodoFilters = dynamic(() => import('../src/components/kea/TodoFilters'), { ssr: false })
8+
const TodoList = dynamic(() => import('../src/components/kea/TodoList'), { ssr: false })
9+
const TodoStats = dynamic(() => import('../src/components/kea/TodoStats'), { ssr: false })
10+
const DemoControls = dynamic(() => import('../src/components/kea/DemoControls'), { ssr: false })
11+
12+
export default function KeaPage() {
13+
const [mounted, setMounted] = useState(false)
14+
15+
useEffect(() => {
16+
// Initialize Kea context with PostHog logger on client side
17+
import('../src/kea-store').then(() => {
18+
setMounted(true)
19+
})
20+
}, [])
21+
22+
return (
23+
<>
24+
<Head>
25+
<title>Kea Todo List</title>
26+
<meta name="description" content="Kea Todo List with PostHog integration" />
27+
<meta name="viewport" content="width=device-width, initial-scale=1" />
28+
<link rel="icon" href="/favicon.ico" />
29+
</Head>
30+
<div className="container">
31+
<h1>Kea Todo List</h1>
32+
<p style={{ marginBottom: '0.5rem', color: '#666' }}>
33+
This page uses Kea instead of Redux for state management.
34+
</p>
35+
<p style={{ marginBottom: '1rem' }}>
36+
<a href="/" style={{ color: '#0070f3', textDecoration: 'none' }}>
37+
← Back to Home
38+
</a>
39+
{' | '}
40+
<a href="/redux" style={{ color: '#0070f3', textDecoration: 'none' }}>
41+
→ Try the Redux version
42+
</a>
43+
</p>
44+
{mounted ? (
45+
<>
46+
<TodoInput />
47+
<TodoFilters />
48+
<TodoList />
49+
<TodoStats />
50+
<DemoControls />
51+
</>
52+
) : (
53+
<p>Loading Kea components...</p>
54+
)}
55+
</div>
56+
</>
57+
)
58+
}

0 commit comments

Comments
 (0)