Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Move route data to Astro.locals #2390

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
db2f072
Add route data to `Astro.locals`
delucis Sep 22, 2024
988c554
Use local instead of props in Starlight’s components
delucis Sep 22, 2024
286e9c8
Refactor route components
delucis Sep 22, 2024
34129ab
Merge branch 'main' into chris/locals-route-data
delucis Oct 19, 2024
9aa7f35
Merge branch 'main' into chris/locals-route-data
delucis Nov 6, 2024
990bc51
Merge branch 'main' into chris/locals-route-data
delucis Dec 14, 2024
28039b4
Fix 404 render
delucis Dec 14, 2024
08c18a7
Make `routeData` a getter to avoid null checks in components
delucis Dec 14, 2024
52a13fe
Move 404 route data logic up out of components
delucis Dec 14, 2024
c1ffade
Fix SSR 404s
delucis Dec 14, 2024
e7ced3d
Rename `routeData` => `starlightRoute`
delucis Dec 14, 2024
8c67752
Merge branch 'main' into chris/locals-route-data
delucis Dec 17, 2024
243786a
Draft docs & schema
delucis Dec 18, 2024
a547e92
Add default and description in middleware config schema
delucis Dec 18, 2024
8f44f89
Tweak proposed middleware API in docs
delucis Dec 18, 2024
2cc0a8f
Move route data docs to dedicated reference page
delucis Dec 18, 2024
c21fba9
Update component overrides guide to match locals usage
delucis Dec 18, 2024
1e79b85
Add reference link to guide
delucis Dec 18, 2024
f150aba
Update config test snapshot
delucis Dec 19, 2024
840fd32
Fix links in English pages
delucis Dec 19, 2024
8db3c72
Merge branch 'main' into chris/locals-route-data
delucis Dec 19, 2024
6d0c68b
Refactor routing utilities to avoid type issues
delucis Dec 19, 2024
67cb8ed
Also move `StarlightRouteData` type
delucis Dec 19, 2024
33e6d53
More type refactoring
delucis Dec 19, 2024
bac78a0
Remove deprecated `labels` prop from route data
delucis Dec 23, 2024
2f19039
Add virtual module that exports route middleware
delucis Dec 23, 2024
f6f9af5
Add new dependency to deep clone route data
delucis Dec 23, 2024
0107e9a
Run user middleware when adding route data to pages
delucis Dec 23, 2024
72a9577
FIx tests
delucis Dec 23, 2024
b362c7a
Remove unused import
delucis Dec 26, 2024
72f3bfb
Merge branch 'main' into chris/locals-route-data
delucis Jan 8, 2025
9aa3461
Add changeset for move of route data to locals
delucis Jan 8, 2025
3a30a0b
Remove props type import in DocSearch component
delucis Jan 9, 2025
503b46d
Step up and Badge on (#2778)
trueberryless Jan 10, 2025
c8e26f8
Create `@astrojs/starlight/route-data` module to export utilities
delucis Jan 10, 2025
4eb3adf
Export `StarlightRouteData` type
delucis Jan 11, 2025
eb0357f
Deprecate the old override props export
delucis Jan 11, 2025
4e1e936
`pnpm format`
delucis Jan 11, 2025
a7eebad
Document `defineRouteMiddleware()` and `StarlightRouteData` type
delucis Jan 11, 2025
7554878
Reorganise `Props` type deprecation to fix JSDoc tooltip
delucis Jan 11, 2025
18faac2
Add a demo route middleware file
delucis Jan 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 are no longer required.
- Update code that access `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>
)}
```

Any plugins you use that add component overrides will also need to be updated at the same time as updating Starlight.
2 changes: 2 additions & 0 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export default defineConfig({
trailingSlash: 'always',
integrations: [
starlight({
// TODO(Chris): Remove the demo middleware
routeMiddleware: './src/demo-middleware.ts',
title: 'Starlight',
logo: {
light: '/src/assets/logo-light.svg',
Expand Down
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
110 changes: 110 additions & 0 deletions docs/src/content/docs/guides/route-data.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
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 it is used, and how you can customize it to modify Starlight’s default behavior.

## What is route data?

Starlight route data is an object constructed for each page of your site by combining the following parts of your project:

1. The Starlight configuration in `astro.config.mjs`
2. The content and frontmatter of the current page

For example, route data includes a `siteTitle` property with a value based on the [`title`](/reference/configuration/#title-required) option in your Starlight configuration and a `toc` property generated from headings in the content for the current page.

Some features can be configured both globally in the Starlight configuration and for a specific page in Markdown frontmatter.
In this case, values set for a specific page in frontmatter override the global or default value.
For example, route data includes a `pagination` object with links to the previous and next pages in the sidebar.
If a page disables one of these, for example by setting `prev: false` in frontmatter, this will be reflected in the `pagination` object.

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

## How is route data used?

All of Starlight’s components have access to route data and use it to decide what to render for each page.
For example, the `siteTitle` string is used to display the site title and the `sidebar` array is used to render the global sidebar navigation.

Route data is available from the `Astro.locals.starlightRoute` global in Astro components:

```astro title="example.astro"
---
const { siteTitle } = Astro.locals.starlightRoute;
---

<p>The title of this site is “{siteTitle}”</p>
```

If you are building [component overrides](/guides/overriding-components/), you can access route data like this 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 middleware 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>
Loading
Loading