Starlight Custom Components
Starlight's built-in UI can be extended or replaced by registering your own Astro components. Use this when CSS is not enough or you need behavioral changes.
When to Use
- Changing a Starlight UI element that CSS alone cannot address
- Adding new UI alongside an existing Starlight element (e.g., a banner above the header)
- Replacing a built-in component with different markup or behavior
- Conditionally rendering different UI on specific pages
When Not to Use
- Simple color or font changes — use
starlight-themeinstead - Adding documentation content (pages, sidebars) — use
starlight-baseinstead
Mindset
Starlight's UI is a set of named slots. You swap slot implementations, not DOM nodes.
- Named slots, not selectors. You register a replacement
.astrofile for a named component slot inastro.config.mjs. No CSS selector is involved — you replace the entire component rendering. - Wrap, don't replace. Import the default component and render it inside your override to add behavior without duplicating Starlight's markup. This keeps you compatible with future Starlight updates.
- Page data lives in
Astro.locals, not props. All route metadata is available viaAstro.locals.starlightRoute. See overrides-reference.md for all available properties.
Step-by-Step: Override a Component
Step 1 — Identify the slot name from the Overrides Reference. Common ones: Header, Footer, PageTitle, Sidebar, TableOfContents, Hero, Pagination. Use the Overrides Map to visually locate UI areas.
Step 2 — Create the component:
---
// src/components/CustomFooter.astro
---
<footer>
<p>Built with Starlight</p>
</footer>
Step 3 — Register in astro.config.mjs:
starlight({
components: {
Footer: './src/components/CustomFooter.astro',
},
})
Wrapping a Built-in Component
---
// src/components/CustomSocialIcons.astro
import Default from '@astrojs/starlight/components/SocialIcons.astro';
---
<a href="mailto:hello@example.com">Contact</a>
<Default><slot /></Default>
For layout components with named slots (PageFrame, TwoColumnContent), transfer them explicitly:
<Default>
<slot />
<slot name="right-sidebar" slot="right-sidebar" />
</Default>
Accessing Page Data
---
const { title, description } = Astro.locals.starlightRoute.entry.data;
const isHomepage = Astro.locals.starlightRoute.id === '';
---
See overrides-reference.md for the full starlightRoute property table.
Anti-Patterns
NEVER register a component path with a leading slash
WHY: Paths in components: {} are relative to the project root. Consequence: Build error — component not found.
BAD: Footer: '/src/components/CustomFooter.astro'
GOOD: Footer: './src/components/CustomFooter.astro'
NEVER omit <slot /> when wrapping a built-in component
WHY: Without <slot />, child content is silently discarded.
BAD: <Default /> (no slot)
GOOD: <Default><slot /></Default>
Consequence: Sidebar links and other nested content disappear.
NEVER access page data via Astro.props in override components
WHY: Override components do not receive page data as props — only via Astro.locals.
BAD: const { title } = Astro.props; — always undefined
GOOD: const { title } = Astro.locals.starlightRoute.entry.data;
Consequence: All page metadata is undefined.
NEVER rely on file name to determine which slot is overridden
WHY: Only the key in components: {} determines the slot, not the file name.
BAD: Naming a file Footer.astro without registering it in config.
GOOD: Footer: './src/components/MyFooter.astro' in config.
Consequence: Default component renders unchanged.
NEVER forget to transfer named slots on layout components
WHY: PageFrame and TwoColumnContent expose named slots. Omitting them silently drops content. Consequence: Right sidebar disappears from all pages.
BAD: Wrap with only <Default><slot /></Default>
GOOD:
<Default>
<slot />
<slot name="right-sidebar" slot="right-sidebar" />
</Default>
NEVER import component internals not listed in the Overrides Reference
WHY: Only listed components are stable public API. Others may change without notice. Consequence: Imports break on minor version bumps.
BAD: Import an internal component not in the official reference. GOOD: Override only components from the official list.