making-scenes-tab-awareoleh posthog
Guides converting PostHog frontend scenes to be tab aware for internal scene tabs. Use when adding or refactoring a `SceneExport` scene, fixing state leaking…
npx skills add https://github.com/posthog/posthog --skill making-scenes-tab-awareMaking scenes tab aware
PostHog's internal tabs keep scene root logics mounted even when a tab is inactive. That only works correctly when the scene is designed for it.
A fully tab-aware scene has three properties:
- The root
SceneExport.logicis scoped per internal tab. - Scene-owned URL sync only reacts for the active tab and updates inactive tabs without hijacking the browser URL.
- Any child logic that must survive React unmounts is attached to the scene root logic.
sceneLogic already injects tabId into both the scene component props and the scene logic props.
See frontend/src/scenes/sceneLogic.tsx.
Use this skill when
- Adding a new scene with
SceneExport - Refactoring an existing scene that loses filters, edit state, or form state on tab switch
- Fixing state bleed between two internal tabs showing the same scene type
- Replacing plain
urlToAction/actionToUrlin scene root logic - Splitting a resource logic into a tab-aware scene root plus attached child logic
Start with an audit
- Run the audit commands in references/audit-commands.md.
- Compare the current scene against the patterns in references/repo-patterns.md.
- Work through the migration checklist in references/migration-checklist.md.
Fast decision guide
Scene is already fully tab aware
Examples:
frontend/src/scenes/insights/insightSceneLogic.tsxfrontend/src/scenes/dashboard/dashboards/dashboardsLogic.tsproducts/endpoints/frontend/endpointSceneLogic.tsxproducts/workflows/frontend/WorkflowsScene.tsx
Usually no structural work is needed. Only follow existing patterns.
Scene is partially tab aware
Examples:
products/actions/frontend/logics/actionLogic.tsfrontend/src/scenes/subscriptions/subscriptionSceneLogic.tsx
These scenes already isolate instances with tabId in their key, but they do not use the scene-level tabAwareScene() contract.
Treat them as good raw material for a full migration.
Scene is not tab aware
Examples:
frontend/src/scenes/feature-flags/featureFlagLogic.tsfrontend/src/scenes/product-tours/productTourLogic.tsfrontend/src/scenes/dashboard/dashboardLogic.tsx
These usually key by resource id, own router bindings directly, or depend on React-local state that disappears on tab switch. They often need a dedicated scene-root logic added on top of the existing resource logic.
Core rule
If a logic is the scene root referenced by SceneExport.logic, it should normally be keyed by tabId and own scene-level router sync.
If a logic is a child resource/editor/detail logic, it can key by tabId + resource id and be attached to the scene root with useAttachedLogic.
What to avoid
- Root scene logic keyed only by resource id
- Plain
urlToAction/actionToUrlon the scene root - URL ownership spread across multiple nested logics
- Important scene state stored in React
useState - Child logics mounted only through the view layer when their state must survive tab switches
Verification
At minimum:
- Open the same scene type in two internal tabs.
- Change scene state in one tab.
- Confirm the other tab keeps its own independent state.
- Switch away and back.
- Confirm edit state, filters, and child-logic state are preserved.
Then run the relevant tests plus:
hogli lint:skills
hogli build:skills