Skip to content

feat(start): include project name in browser tab title#352

Open
raonitimo wants to merge 1 commit intoOpenpanel-dev:mainfrom
raonitimo:fix/page-title-project-name
Open

feat(start): include project name in browser tab title#352
raonitimo wants to merge 1 commit intoOpenpanel-dev:mainfrom
raonitimo:fix/page-title-project-name

Conversation

@raonitimo
Copy link
Copy Markdown
Contributor

@raonitimo raonitimo commented Apr 28, 2026

Closes #177.

Problem

Every browser tab reads <Page> | OpenPanel.dev regardless of which project is open, so users with multiple projects pinned can't tell tabs apart at a glance.

Approach

The existing createProjectTitle() helper in apps/start/src/utils/title.ts already accepts an optional projectName arg — it just isn't passed by any caller. Threading it through every route's loader + head() would touch ~30 files.

Instead, this adds a single useProjectDocumentTitle hook mounted in the project layout (_app.$organizationId.$projectId.tsx) that:

  1. Reads the project from the existing tRPC cache (now prefetched in the layout's loader, alongside the organization).
  2. Patches document.title on the client whenever it changes — observed via a MutationObserver on <head> so it re-applies after route navigation and when the project query first resolves.

Result on a project named MyProject:

  • Before: Dashboard | OpenPanel.dev
  • After: Dashboard | MyProject | OpenPanel.dev

SSR title is unchanged — this is purely a browser tab UX improvement.

Files

  • apps/start/src/hooks/use-project-document-title.ts — new hook
  • apps/start/src/routes/_app.$organizationId.$projectId.tsx — prefetch project in loader, mount hook

Verified

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 → workspace Acme Inc → project MyProject):

URL document.title
/acme-inc (org page, outside project context) Projects | Organization | OpenPanel.dev (unchanged — hook unmounts)
/acme-inc/myproject Dashboard | MyProject | OpenPanel.dev
/acme-inc/myproject/realtime Realtime | MyProject | OpenPanel.dev
/acme-inc/myproject/events Events | 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.

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"
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR implements document title synchronization with project names. A new useProjectDocumentTitle hook applies deterministic title transformations and observes mutations to keep them synchronized. The route component integrates this hook and prefetches project data concurrently with organization data.

Changes

Cohort / File(s) Summary
New Title Synchronization Hook
apps/start/src/hooks/use-project-document-title.ts
Adds useProjectDocumentTitle hook that transforms document.title to include project name when it ends with | OpenPanel.dev. Uses MutationObserver to detect title changes and maintains sync state via ref to prevent redundant DOM writes.
Route Integration
apps/start/src/routes/_app.$organizationId.$projectId.tsx
Updates route loader to concurrently prefetch organization and project data. ProjectDashboard component now calls useProjectDocumentTitle(project?.name) to update page title, and reads projectId from route params for data fetching.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A project's name now crowns the page,
No more generic "Overview" stage,
The title dances, synced with care,
As mutations ripple through the air,
Sweet metadata, finally there!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main objective: adding project name to the browser tab title.
Linked Issues check ✅ Passed The implementation successfully addresses issue #177 by adding project name to document.title via useProjectDocumentTitle hook, resolving the feature request.
Out of Scope Changes check ✅ Passed All changes directly support the stated objective of including project names in browser tab titles; no unrelated modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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_SUFFIX is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 892bab5 and aa34692.

📒 Files selected for processing (2)
  • apps/start/src/hooks/use-project-document-title.ts
  • apps/start/src/routes/_app.$organizationId.$projectId.tsx

@raonitimo
Copy link
Copy Markdown
Contributor Author

Screenshot 2026-04-28 at 1 53 31 PM

@lindesvard
Copy link
Copy Markdown
Contributor

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 apps/start/src/utils/title.ts instead of redefining it again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Set title to project name

2 participants