diff --git a/app/components/PostHog.tsx b/app/components/PostHog.tsx
new file mode 100644
index 0000000..e45ea9f
--- /dev/null
+++ b/app/components/PostHog.tsx
@@ -0,0 +1,26 @@
+"use client"
+
+import posthog from "posthog-js"
+import { useEffect } from "react"
+
+export function PostHog() {
+ useEffect(() => {
+ if (location.hostname !== "docs.gitbutler.com") return
+ posthog.init("phc_yJx46mXv6kA5KTuM2eEQ6IwNTgl5YW3feKV5gi7mfGG", {
+ api_host: "https://eu.posthog.com",
+ defaults: "2026-06-25",
+ cookie_persisted_properties: ["app_distinct_id"]
+ })
+ recordAppDistinctId()
+ }, [])
+ return null
+}
+
+function recordAppDistinctId() {
+ const url = new URL(location.href)
+ const appDistinctId = url.searchParams.get("did")
+ if (!appDistinctId) return
+ posthog.register({ app_distinct_id: appDistinctId })
+ url.searchParams.delete("did")
+ history.replaceState(history.state, "", url)
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index 453b567..a65351c 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -6,6 +6,7 @@ import type { ReactNode } from "react"
import "@gitbutler/design-core/fonts"
import Script from "next/script"
+import { PostHog } from "@/app/components/PostHog"
import WebMcpTools from "@/app/components/WebMcpTools"
import { getSiteUrl } from "@/app/utils/site"
@@ -21,6 +22,7 @@ export default function Layout({ children }: { children: ReactNode }) {
return (
+
{children}
diff --git a/package.json b/package.json
index ec5380a..98e0463 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"glob": "^12.0.0",
"next": "15.5.18",
"pdfkit": "^0.17.2",
+ "posthog-js": "1.417.1",
"prettier": "^3.3.3",
"puppeteer": "^24.36.1",
"react": "^18.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 914a0f7..8ae3f21 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -50,6 +50,9 @@ importers:
pdfkit:
specifier: ^0.17.2
version: 0.17.2
+ posthog-js:
+ specifier: 1.417.1
+ version: 1.417.1
prettier:
specifier: ^3.3.3
version: 3.3.3
@@ -712,6 +715,15 @@ packages:
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ '@posthog/browser-common@0.5.0':
+ resolution: {integrity: sha512-8DaxVZS1bQPbA514RePurLNbYjei3P4jhnC206DwVv5XThmZM3QdlsXenI2ujE3pLbgQ79hYn9o1Kda8I3WK/Q==}
+
+ '@posthog/core@1.48.4':
+ resolution: {integrity: sha512-Bff4EVy6x1FrJIdhrEZYGJuyarwZkKC0fOncRobpPgJWMQc83W2U/6ChYPQYcA1Vp3wzAOVCOSigs6fMqojM5Q==}
+
+ '@posthog/types@1.405.0':
+ resolution: {integrity: sha512-4rZ/taVXKQxs9Jrf7ZjlCRgrOSL69oKAgIWJQa5kRNJ6wll1UANbrJTSY+Su1e88LIG4zZVjKKKjyQHCkHdHcw==}
+
'@puppeteer/browsers@2.11.2':
resolution: {integrity: sha512-GBY0+2lI9fDrjgb5dFL9+enKXqyOPok9PXg/69NVkjW3bikbK9RQrNrI3qccQXmDNN7ln4j/yL89Qgvj/tfqrw==}
engines: {node: '>=18'}
@@ -2001,6 +2013,9 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ core-js@3.50.0:
+ resolution: {integrity: sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==}
+
cose-base@1.0.3:
resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
@@ -2299,6 +2314,9 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+ dompurify@3.4.14:
+ resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==}
+
dompurify@3.4.3:
resolution: {integrity: sha512-VVwJidIJcp1hpg2OMXML3ZVRPYSZiq4aX7qBh83BSIpOaRDqI+qxhXjjIWnpzkOXhmp0L81lnoME1mnCc9H48A==}
@@ -2647,6 +2665,9 @@ packages:
fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+ fflate@0.4.9:
+ resolution: {integrity: sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw==}
+
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@@ -3800,6 +3821,17 @@ packages:
resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==}
engines: {node: ^10 || ^12 || >=14}
+ posthog-js@1.417.1:
+ resolution: {integrity: sha512-QpZnHA2PieGq9+W03LBw23DnN9CD1zM37DcXnpZkg+C5iAwYXLiZs8qof0Yi/BhY35PYuhVmv93nhztyHjVaJw==}
+
+ preact@10.29.8:
+ resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==}
+ peerDependencies:
+ preact-render-to-string: '>=5'
+ peerDependenciesMeta:
+ preact-render-to-string:
+ optional: true
+
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -3904,6 +3936,9 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ query-selector-shadow-dom@1.0.1:
+ resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==}
+
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -4564,6 +4599,12 @@ packages:
vfile@6.0.1:
resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
+ web-vitals@5.3.0:
+ resolution: {integrity: sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==}
+
+ web-vitals@6.0.0:
+ resolution: {integrity: sha512-Guaibvy/+uNtL6Bsu4jmMJGzuSl91oeRH5iO9pPRbYftnFUr3yqT1TUNX/OE4o9HexuEMU3Kb/Wg7iKhlffZUA==}
+
webdriver-bidi-protocol@0.4.0:
resolution: {integrity: sha512-U9VIlNRrq94d1xxR9JrCEAx5Gv/2W7ERSv8oWRoNe/QYbfccS0V3h/H6qeNeCRJxXGMhhnkqvwNrvPAYeuP9VA==}
@@ -5099,6 +5140,17 @@ snapshots:
'@pkgr/core@0.1.1': {}
+ '@posthog/browser-common@0.5.0':
+ dependencies:
+ '@posthog/core': 1.48.4
+ '@posthog/types': 1.405.0
+
+ '@posthog/core@1.48.4':
+ dependencies:
+ '@posthog/types': 1.405.0
+
+ '@posthog/types@1.405.0': {}
+
'@puppeteer/browsers@2.11.2':
dependencies:
debug: 4.4.3
@@ -6537,6 +6589,8 @@ snapshots:
concat-map@0.0.1: {}
+ core-js@3.50.0: {}
+
cose-base@1.0.3:
dependencies:
layout-base: 1.0.2
@@ -6847,6 +6901,10 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dompurify@3.4.14:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
dompurify@3.4.3:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -7383,6 +7441,8 @@ snapshots:
dependencies:
pend: 1.2.0
+ fflate@0.4.9: {}
+
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@@ -8987,6 +9047,23 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ posthog-js@1.417.1:
+ dependencies:
+ '@posthog/browser-common': 0.5.0
+ '@posthog/core': 1.48.4
+ '@posthog/types': 1.405.0
+ core-js: 3.50.0
+ dompurify: 3.4.14
+ fflate: 0.4.9
+ preact: 10.29.8
+ query-selector-shadow-dom: 1.0.1
+ web-vitals: 5.3.0
+ web-vitals-soft-navs: web-vitals@6.0.0
+ transitivePeerDependencies:
+ - preact-render-to-string
+
+ preact@10.29.8: {}
+
prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0:
@@ -9067,6 +9144,8 @@ snapshots:
- typescript
- utf-8-validate
+ query-selector-shadow-dom@1.0.1: {}
+
queue-microtask@1.2.3: {}
queue@6.0.2:
@@ -9916,6 +9995,10 @@ snapshots:
unist-util-stringify-position: 4.0.0
vfile-message: 4.0.2
+ web-vitals@5.3.0: {}
+
+ web-vitals@6.0.0: {}
+
webdriver-bidi-protocol@0.4.0: {}
which-boxed-primitive@1.0.2: