Files
supabase/apps/studio/lib/page-title.ts
Danny White 96ac1c2652 feat(studio): page titles (core) (#43538)
## What kind of change does this PR introduce?

- Resolves FE-1960
- Resolves FE-1983
- Resolves DEPR-207

## What is the current behavior?

Page titles between surfaces are inconsistent and vague. Sometimes they
say the product name:

```
My Project | My Org | Supabase
```

...even when on a specific surface like Database > Tables.

Other times they show the entity name but skip over the project or org
name :

```
Edge Functions | Supabase
```

## What is the new behavior?

Page titles *mostly* (see below) follow the same format:
```
users | Table Editor | My Project | My Org | Supabase
hello-world | Logs | Edge Functions | My Project | My Org | Supabase
Backups | Database | My Project | My Org | Supabase
Authentication | My Project | My Org | Supabase
```

That format is:

entity, section, surface, project, org, brand

## Additional context

This is stacked PR 1/5 for page title improvements. Includes the core
title utility and ProjectLayout integration/tests. Follow-up stacked PRs
are based on this branch:

- https://github.com/supabase/supabase/pull/43534
- https://github.com/supabase/supabase/pull/43535
- https://github.com/supabase/supabase/pull/43536
- https://github.com/supabase/supabase/pull/43537

This one should be merged first. The others (listed right above) can
_then_ be merged in any order.
2026-03-09 15:25:05 +07:00

47 lines
1.1 KiB
TypeScript

export interface StudioPageTitleParts {
entity?: string
section?: string
surface?: string
project?: string
org?: string
brand?: string
}
export const STUDIO_PAGE_TITLE_SEPARATOR = ' | '
const MAX_SEGMENT_LENGTH = 60
const normalizeTitleSegment = (value?: string) => {
if (value === undefined) return undefined
const normalized = value.trim().replace(/\s+/g, ' ')
if (normalized.length === 0) return undefined
if (normalized.length <= MAX_SEGMENT_LENGTH) return normalized
return `${normalized.slice(0, MAX_SEGMENT_LENGTH - 1).trimEnd()}`
}
export const buildStudioPageTitle = (parts: StudioPageTitleParts) => {
const orderedParts = [
parts.entity,
parts.section,
parts.surface,
parts.project,
parts.org,
parts.brand,
]
const segments: string[] = []
orderedParts.forEach((part) => {
const segment = normalizeTitleSegment(part)
if (!segment) return
const lastSegment = segments[segments.length - 1]
if (lastSegment !== undefined && lastSegment.toLowerCase() === segment.toLowerCase()) return
segments.push(segment)
})
return segments.join(STUDIO_PAGE_TITLE_SEPARATOR)
}