Skip to content

Commit

Permalink
Move route data to Astro.locals (#2390)
Browse files Browse the repository at this point in the history
Co-authored-by: Chris Swithinbank <[email protected]>
Co-authored-by: HiDeoo <[email protected]>
Co-authored-by: trueberryless <[email protected]>
  • Loading branch information
3 people authored Feb 15, 2025
1 parent f895f75 commit f493361
Show file tree
Hide file tree
Showing 78 changed files with 1,087 additions and 540 deletions.
36 changes: 36 additions & 0 deletions .changeset/chilled-bees-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
'@astrojs/starlight': minor
---

Moves route data to `Astro.locals` instead of passing it down via component props

⚠️ **Breaking change:**
Previously, all of Starlight’s templating components, including user or plugin overrides, had access to a data object for the current route via `Astro.props`.
This data is now available as `Astro.locals.starlightRoute` instead.

To update, refactor any component overrides you have:

- Remove imports of `@astrojs/starlight/props`, which is now deprecated.
- Update code that accesses `Astro.props` to use `Astro.locals.starlightRoute` instead.
- Remove any spreading of `{...Astro.props}` into child components, which is no longer required.

In the following example, a custom override for Starlight’s `LastUpdated` component is updated for the new style:

```diff
---
import Default from '@astrojs/starlight/components/LastUpdated.astro';
- import type { Props } from '@astrojs/starlight/props';

- const { lastUpdated } = Astro.props;
+ const { lastUpdated } = Astro.locals.starlightRoute;

const updatedThisYear = lastUpdated?.getFullYear() === new Date().getFullYear();
---

{updatedThisYear && (
- <Default {...Astro.props}><slot /></Default>
+ <Default><slot /></Default>
)}
```

_Community Starlight plugins may also need to be manually updated to work with Starlight 0.32. If you encounter any issues, please reach out to the plugin author to see if it is a known issue or if an updated version is being worked on._
2 changes: 1 addition & 1 deletion docs/src/components/sidebar-preview.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
} from '../../../packages/starlight/schemas/sidebar';
import SidebarSublist from '../../../packages/starlight/components/SidebarSublist.astro';
import type { Badge } from '../../../packages/starlight/schemas/badge';
import type { SidebarEntry } from '../../../packages/starlight/utils/navigation';
import type { SidebarEntry } from '../../../packages/starlight/utils/routing/types';
interface Props {
config: SidebarConfig;
Expand Down
38 changes: 16 additions & 22 deletions docs/src/content/docs/guides/overriding-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ Overriding Starlight’s default components can be useful when:
```astro
---
// src/components/EmailLink.astro
import type { Props } from '@astrojs/starlight/props';
const email = '[email protected]';
---
<a href="mailto:[email protected]">E-mail Me</a>
<a href=`mailto:${email}`>E-mail Me</a>
```

3. Tell Starlight to use your custom component in the [`components`](/reference/configuration/#components) configuration option in `astro.config.mjs`:
Expand Down Expand Up @@ -70,52 +71,46 @@ You can build with Starlight’s default UI components just as you would with yo

The example below shows a custom component that renders an e-mail link along with the default `SocialIcons` component:

```astro {4,8}
```astro {3,7}
---
// src/components/EmailLink.astro
import type { Props } from '@astrojs/starlight/props';
import Default from '@astrojs/starlight/components/SocialIcons.astro';
---
<a href="mailto:[email protected]">E-mail Me</a>
<Default {...Astro.props}><slot /></Default>
<Default><slot /></Default>
```

When rendering a built-in component inside a custom component:

- Spread `Astro.props` into it. This makes sure that it receives all the data it needs to render.
- Add a [`<slot />`](https://docs.astro.build/en/basics/astro-components/#slots) inside the default component. This makes sure that if the component is passed any child elements, Astro knows where to render them.
When rendering a built-in component inside a custom component add a [`<slot />`](https://docs.astro.build/en/basics/astro-components/#slots) inside the default component. This makes sure that if the component is passed any child elements, Astro knows where to render them.

If you are reusing the [`PageFrame`](/reference/overrides/#pageframe) or [`TwoColumnContent`](/reference/overrides/#twocolumncontent) components which contain [named slots](https://docs.astro.build/en/basics/astro-components/#named-slots), you also need to [transfer](https://docs.astro.build/en/basics/astro-components/#transferring-slots) these slots as well.

The example below shows a custom component that reuses the `TwoColumnContent` component which contains an additional `right-sidebar` named slot that needs to be transferred:

```astro {9}
```astro {8}
---
// src/components/CustomContent.astro
import type { Props } from '@astrojs/starlight/props';
import Default from '@astrojs/starlight/components/TwoColumnContent.astro';
---
<Default {...Astro.props}>
<Default>
<slot />
<slot name="right-sidebar" slot="right-sidebar" />
</Default>
```

## Use page data

When overriding a Starlight component, your custom implementation receives a standard `Astro.props` object containing all the data for the current page.
When overriding a Starlight component, you can access the global [`starlightRoute` object](/guides/route-data/) containing all the data for the current page.
This allows you to use these values to control how your component template renders.

For example, you can read the page’s frontmatter values as `Astro.props.entry.data`. In the following example, a replacement [`PageTitle`](/reference/overrides/#pagetitle) component uses this to display the current page’s title:
In the following example, a replacement [`PageTitle`](/reference/overrides/#pagetitle) component displays the current page’s title as set in the content’s frontmatter:

```astro {5} "{title}"
```astro {4} "{title}"
---
// src/components/Title.astro
import type { Props } from '@astrojs/starlight/props';
const { title } = Astro.props.entry.data;
const { title } = Astro.locals.starlightRoute.entry.data;
---
<h1 id="_top">{title}</h1>
Expand All @@ -127,28 +122,27 @@ const { title } = Astro.props.entry.data;
</style>
```

Learn more about all the available props in the [Overrides Reference](/reference/overrides/#component-props).
Learn more about all the available properties in the [Route Data Reference](/reference/route-data/).

### Only override on specific pages

Component overrides apply to all pages. However, you can conditionally render using values from `Astro.props` to determine when to show your custom UI, when to show Starlight’s default UI, or even when to show something entirely different.
Component overrides apply to all pages. However, you can conditionally render using values from `starlightRoute` to determine when to show your custom UI, when to show Starlight’s default UI, or even when to show something entirely different.

In the following example, a component overriding Starlight's [`Footer`](/reference/overrides/#footer-1) displays "Built with Starlight 🌟" on the homepage only, and otherwise shows the default footer on all other pages:

```astro
---
// src/components/ConditionalFooter.astro
import type { Props } from '@astrojs/starlight/props';
import Default from '@astrojs/starlight/components/Footer.astro';
const isHomepage = Astro.props.id === '';
const isHomepage = Astro.locals.starlightRoute.id === '';
---
{
isHomepage ? (
<footer>Built with Starlight 🌟</footer>
) : (
<Default {...Astro.props}>
<Default>
<slot />
</Default>
)
Expand Down
139 changes: 139 additions & 0 deletions docs/src/content/docs/guides/route-data.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
title: Route Data
description: Learn how Starlight’s page data model is used to render your pages and how you can customize it.
---

import { Steps } from '@astrojs/starlight/components';

When Starlight renders a page in your documentation, it first creates a route data object to represent what is on that page.
This guide explains how route data is generated, how to use it, and how you can customize it to modify Starlight’s default behavior.

See the [“Route Data Reference”](/reference/route-data/) for a full list of the available properties.

## What is route data?

Starlight route data is an object containing all the information required to render a single page.
It includes information for the current page as well as data generated from your Starlight configuration.

## Using route data

All of Starlight’s components use route data to decide what to render for each page.
For example, the [`siteTitle`](/reference/route-data/#sitetitle) string is used to display the site title and the [`sidebar`](/reference/route-data/#sidebar) array is used to render the global sidebar navigation.

You can access this data from the `Astro.locals.starlightRoute` global in Astro components:

```astro title="example.astro" {2}
---
const { siteTitle } = Astro.locals.starlightRoute;
---
<p>The title of this site is “{siteTitle}”</p>
```

This can be useful for example when building [component overrides](/guides/overriding-components/) to customize what you display.

## Customizing route data

Starlight’s route data works out of the box and does not require any configuration.
However, for advanced use cases, you may want to customize route data for some or all pages to modify how your site displays.

This is a similar concept to [component overrides](/guides/overriding-components/), but instead of modifying how Starlight renders your data, you modify the data Starlight renders.

### When to customize route data

Customizing route data can be useful when you want to modify how Starlight processes your data in a way not possible with existing configuration options.

For example, you may want to filter sidebar items or customize titles for specific pages.
Changes like this do not require modifying Starlight’s default components, only the data passed to those components.

### How to customize route data

You can customize route data using a special form of “middleware”.
This is a function that is called every time Starlight renders a page and can modify values in the route data object.

<Steps>

1. Create a new file exporting an `onRequest` function using Starlight’s `defineRouteMiddleware()` utility:

```ts
// src/routeData.ts
import { defineRouteMiddleware } from '@astrojs/starlight/route-data';

export const onRequest = defineRouteMiddleware(() => {});
```

2. Tell Starlight where your route data middleware file is located in `astro.config.mjs`:

```js ins={9}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';

export default defineConfig({
integrations: [
starlight({
title: 'My delightful docs site',
routeMiddleware: './src/routeData.ts',
}),
],
});
```

3. Update your `onRequest` function to modify route data.

The first argument your middleware will receive is [Astro’s `context` object](https://docs.astro.build/en/reference/api-reference/).
This contains full information about the current page render, including the current URL and `locals`.

In this example, we are going to make our docs more exciting by adding an exclamation mark to the end of every page’s title.

```ts
// src/routeData.ts
import { defineRouteMiddleware } from '@astrojs/starlight/route-data';

export const onRequest = defineRouteMiddleware((context) => {
// Get the content collection entry for this page.
const { entry } = context.locals.starlightRoute;
// Update the title to add an exclamation mark.
entry.data.title = entry.data.title + '!';
});
```

</Steps>

#### Multiple route middleware

Starlight also supports providing multiple middleware.
Set `routeMiddleware` to an array of paths to add more than one middleware handler:

```js {9}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';

export default defineConfig({
integrations: [
starlight({
title: 'My site with multiple middleware',
routeMiddleware: ['./src/middleware-one.ts', './src/middleware-two.ts'],
}),
],
});
```

#### Waiting for later route middleware

To wait for middleware later in the stack to run before executing your code, you can await the `next()` callback passed as the second argument to your middleware function.
This can be useful to wait for a plugin’s middleware to run before making changes for example.

```ts "next" "await next();"
// src/routeData.ts
import { defineRouteMiddleware } from '@astrojs/starlight/route-data';

export const onRequest = defineRouteMiddleware(async (context, next) => {
// Wait for later middleware to run.
await next();
// Modify route data.
const { entry } = context.locals.starlightRoute;
entry.data.title = entry.data.title + '!';
});
```
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/sidebar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ The configuration above generates the following sidebar:
Starlight can automatically generate a group in your sidebar based on a directory of your docs.
This is helpful when you do not want to manually enter each sidebar item in a group.

By default, pages are sorted in alphabetical order according to the file [`slug`](/reference/overrides/#slug).
By default, pages are sorted in alphabetical order according to the file [`slug`](/reference/route-data/#slug).

Add an autogenerated group using an object with `label` and `autogenerate` properties. Your `autogenerate` configuration must specify the `directory` to use for sidebar entries. For example, with the following configuration:

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/hi/guides/overriding-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const { title } = Astro.props.entry.data;
</style>
```

[ओवरराइड्स संदर्भ](/hi/reference/overrides/#component-props) में सभी उपलब्ध प्रॉप्स के बारे में और जानें।
[ओवरराइड्स संदर्भ](/hi/reference/route-data/) में सभी उपलब्ध प्रॉप्स के बारे में और जानें।

### केवल विशिष्ट पृष्ठों पर ही ओवरराइड करें

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/hi/guides/sidebar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ starlight({
Starlight स्वचालित रूप से आपके दस्तावेज़ों की निर्देशिका के आधार पर आपके साइडबार में एक समूह उत्पन्न कर सकता है।
यह तब सहायक होता है जब आप किसी समूह में प्रत्येक साइडबार आइटम को मैन्युअल रूप से दर्ज नहीं करना चाहते हैं।

डिफ़ॉल्ट रूप से, पेजों को फ़ाइल [`slug`](/hi/reference/overrides/#slug) के अनुसार वर्णानुक्रम में क्रमबद्ध किया जाता है।
डिफ़ॉल्ट रूप से, पेजों को फ़ाइल [`slug`](/hi/reference/route-data/#slug) के अनुसार वर्णानुक्रम में क्रमबद्ध किया जाता है।

`label` और `autogenerate` गुणों वाले ऑब्जेक्ट का उपयोग करके एक स्वतः निर्मित समूह जोड़ें। साइडबार प्रविष्टियों के लिए उपयोग करने के लिए आपके `autogenerate` कॉन्फ़िगरेशन को `directory` निर्दिष्ट करना होगा। उदाहरण के लिए, निम्नलिखित कॉन्फ़िगरेशन के साथ:

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/id/guides/overriding-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const { title } = Astro.props.entry.data;
</style>
```

Pelajari lebih lanjut tentang semua prop yang tersedia di [Referensi Penggantian](/id/reference/overrides/#component-props).
Pelajari lebih lanjut tentang semua prop yang tersedia di [Referensi Penggantian](/id/reference/route-data/).

### Mengganti komponen hanya pada halaman tertentu

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/id/guides/sidebar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Starlight dapat secara otomatis membuat grup di sidebar Anda berdasarkan direkto
Ini berguna ketika Anda tidak ingin memasukkan setiap item sidebar secara manual ke dalam grup.
Halaman akan diurutkan secara alfabetis berdasarkan nama file secara default.

Secara default, halaman diurutkan berdasarkan abjad menurut file [`slug`](/id/reference/overrides/#slug).
Secara default, halaman diurutkan berdasarkan abjad menurut file [`slug`](/id/reference/route-data/#slug).

Tambahkan grup yang dihasilkan secara otomatis menggunakan objek dengan properti `label` dan `autogenerate`. Konfigurasi `autogenerate` Anda harus menentukan `directory` yang akan digunakan untuk entri sidebar. Sebagai contoh, dengan konfigurasi berikut:

Expand Down
Loading

0 comments on commit f493361

Please sign in to comment.