feat(start): include project name in browser tab title#352
feat(start): include project name in browser tab title#352raonitimo wants to merge 1 commit intoOpenpanel-dev:mainfrom
Conversation
Closes Openpanel-dev#177. Browser tabs previously read e.g. "Dashboard | OpenPanel.dev" regardless of which project was open, making it hard to tell tabs apart when multiple projects are pinned in the same window. The existing `createProjectTitle()` helper already accepts an optional projectName argument, but no caller passed it. Threading project name through every route's loader/head would touch ~30 files; instead, this adds a single `useProjectDocumentTitle` hook mounted in the project layout that patches `document.title` on the client whenever it changes. The hook observes mutations on `<head>` so it re-applies the project name after navigation (when TanStack's head() re-renders the title) and when the project query first resolves. SSR title is unchanged — this is purely a browser tab UX improvement. Result: "Dashboard | MyProject | OpenPanel.dev"
📝 WalkthroughWalkthroughThis PR implements document title synchronization with project names. A new Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/start/src/hooks/use-project-document-title.ts (1)
3-9: Use shared title-format source instead of duplicating suffix literal.
BASE_SUFFIXis hardcoded here, so this hook can drift from centralized title formatting (apps/start/src/utils/title.ts) if branding/suffix conventions change. Prefer importing a shared constant/helper.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/start/src/hooks/use-project-document-title.ts` around lines 3 - 9, The hook currently defines a hardcoded BASE_SUFFIX and the inject function uses it; replace the literal by importing and using the shared title formatting constant or helper from the centralized title utility (use the exported constant name such as TITLE_SUFFIX or the exported format helper) so inject delegates to or composes with that shared value/function instead of BASE_SUFFIX; modify the inject implementation to call the shared helper or use the shared constant and remove the local BASE_SUFFIX to avoid duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/start/src/hooks/use-project-document-title.ts`:
- Around line 3-9: The hook currently defines a hardcoded BASE_SUFFIX and the
inject function uses it; replace the literal by importing and using the shared
title formatting constant or helper from the centralized title utility (use the
exported constant name such as TITLE_SUFFIX or the exported format helper) so
inject delegates to or composes with that shared value/function instead of
BASE_SUFFIX; modify the inject implementation to call the shared helper or use
the shared constant and remove the local BASE_SUFFIX to avoid duplication.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: aba94f0a-213e-425f-a258-ad0b9e57a65a
📒 Files selected for processing (2)
apps/start/src/hooks/use-project-document-title.tsapps/start/src/routes/_app.$organizationId.$projectId.tsx
|
I like it since we dont need to do this in every route as you mentioned. A nitpick would be to export the base title in |

Closes #177.
Problem
Every browser tab reads
<Page> | OpenPanel.devregardless of which project is open, so users with multiple projects pinned can't tell tabs apart at a glance.Approach
The existing
createProjectTitle()helper inapps/start/src/utils/title.tsalready accepts an optionalprojectNamearg — it just isn't passed by any caller. Threading it through every route'sloader+head()would touch ~30 files.Instead, this adds a single
useProjectDocumentTitlehook mounted in the project layout (_app.$organizationId.$projectId.tsx) that:document.titleon the client whenever it changes — observed via aMutationObserveron<head>so it re-applies after route navigation and when the project query first resolves.Result on a project named
MyProject:Dashboard | OpenPanel.devDashboard | MyProject | OpenPanel.devSSR title is unchanged — this is purely a browser tab UX improvement.
Files
apps/start/src/hooks/use-project-document-title.ts— new hookapps/start/src/routes/_app.$organizationId.$projectId.tsx— prefetch project in loader, mount hookVerified
pnpm --filter start typecheck— clean.End-to-end in a real browser against a local stack (Postgres / Redis / ClickHouse via docker-compose,
pnpm dev, fresh signup → workspaceAcme Inc→ projectMyProject):document.title/acme-inc(org page, outside project context)Projects | Organization | OpenPanel.dev(unchanged — hook unmounts)/acme-inc/myprojectDashboard | MyProject | OpenPanel.dev✅/acme-inc/myproject/realtimeRealtime | MyProject | OpenPanel.dev✅/acme-inc/myproject/eventsEvents | MyProject | OpenPanel.dev✅Confirms (a) project name is injected on every project route, (b) the hook re-applies on intra-project navigation via the
MutationObserver, and (c) it cleanly falls back outside the project layout.