Smart page title component for SvelteKit. Combines your titles into hierarchies like "Billing • Settings • App" based on route structure.
npm install svelte-title
First, set up your root layout:
<!-- src/routes/+layout.svelte -->
<script>
import { page } from '$app/state'
import { Title, resetLevelCounter } from 'svelte-title'
$effect(() => {
page.url.pathname
resetLevelCounter()
})
</script>
<Title title="App" />
Then add titles anywhere:
<script>
import { Title } from 'svelte-title'
</script>
<Title title="Dashboard" />
<!-- Result: "Dashboard • App" -->
Nested layouts work too:
<!-- settings/+layout.svelte -->
<Title title="Settings" />
<!-- settings/billing/+page.svelte -->
<Title title="Billing" />
<!-- Result: "Billing • Settings • App" -->
Want something other than the default bullet (•
)? Pass a separator
prop to your root layout:
<!-- src/routes/+layout.svelte -->
<Title title="App" separator=" | " />
Now all your titles will use pipes:
<Title title="Settings" />
<!-- Result: "Settings | App" -->
The <Title>
component takes these props:
title
(required) - The title textseparator
(optional) - Custom separator (root layout only)override
(optional) - Show only this title, no cascadinglevel
(optional) - Force a specific hierarchy level
<!-- Override mode - just shows "Standalone" -->
<Title title="Standalone" override={true} />
The component automatically detects hierarchy based on render order. Root layouts get level 0, nested layouts get level 1+, and pages get the highest levels. Titles build from specific to general.
Don't put multiple <Title>
components in the same file - use one per layout/page.
- CSR-only mode: Requires fallback
<title>
inapp.html
to prevent showing domain name during JS load - SSR mode: Static
<title>
tags inapp.html
will override the component's SSR-rendered titles - Route changes: Requires
resetLevelCounter()
in root layout for consistent automatic level assignment
- SvelteKit 2.0+
- Svelte 5.0+ (uses runes)
Found a bug or have a feature request? Please open an issue on GitHub.
MIT