-
-
Notifications
You must be signed in to change notification settings - Fork 838
Move route data to Astro.locals
#2390
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
Merged
Merged
Changes from 51 commits
Commits
Show all changes
73 commits
Select commit
Hold shift + click to select a range
db2f072
Add route data to `Astro.locals`
delucis 988c554
Use local instead of props in Starlight’s components
delucis 286e9c8
Refactor route components
delucis 34129ab
Merge branch 'main' into chris/locals-route-data
delucis 9aa7f35
Merge branch 'main' into chris/locals-route-data
delucis 990bc51
Merge branch 'main' into chris/locals-route-data
delucis 28039b4
Fix 404 render
delucis 08c18a7
Make `routeData` a getter to avoid null checks in components
delucis 52a13fe
Move 404 route data logic up out of components
delucis c1ffade
Fix SSR 404s
delucis e7ced3d
Rename `routeData` => `starlightRoute`
delucis 8c67752
Merge branch 'main' into chris/locals-route-data
delucis 243786a
Draft docs & schema
delucis a547e92
Add default and description in middleware config schema
delucis 8f44f89
Tweak proposed middleware API in docs
delucis 2cc0a8f
Move route data docs to dedicated reference page
delucis c21fba9
Update component overrides guide to match locals usage
delucis 1e79b85
Add reference link to guide
delucis f150aba
Update config test snapshot
delucis 840fd32
Fix links in English pages
delucis 8db3c72
Merge branch 'main' into chris/locals-route-data
delucis 6d0c68b
Refactor routing utilities to avoid type issues
delucis 67cb8ed
Also move `StarlightRouteData` type
delucis 33e6d53
More type refactoring
delucis bac78a0
Remove deprecated `labels` prop from route data
delucis 2f19039
Add virtual module that exports route middleware
delucis f6f9af5
Add new dependency to deep clone route data
delucis 0107e9a
Run user middleware when adding route data to pages
delucis 72a9577
FIx tests
delucis b362c7a
Remove unused import
delucis 72f3bfb
Merge branch 'main' into chris/locals-route-data
delucis 9aa3461
Add changeset for move of route data to locals
delucis 3a30a0b
Remove props type import in DocSearch component
delucis 503b46d
Step up and Badge on (#2778)
trueberryless c8e26f8
Create `@astrojs/starlight/route-data` module to export utilities
delucis 4eb3adf
Export `StarlightRouteData` type
delucis eb0357f
Deprecate the old override props export
delucis 4e1e936
`pnpm format`
delucis a7eebad
Document `defineRouteMiddleware()` and `StarlightRouteData` type
delucis 7554878
Reorganise `Props` type deprecation to fix JSDoc tooltip
delucis 18faac2
Add a demo route middleware file
delucis e5df5ec
Merge branch 'main' into chris/locals-route-data
delucis 3c91de4
Use `AstroError` for invalid plugin config mutation
delucis 0ec6e4e
Add support for `next()` callback to middleware system
delucis bb35c02
Implement `addRouteMiddleware()` plugin API
delucis b4acdb6
Add tests
delucis 621a59e
Document support for multiple user middleware and use of `next()`
delucis 3cb40cd
Document `addRouteMiddleware()` plugin API
delucis b87650c
Fix `injectTranslations()` type in plugin quick API docs
delucis 15da794
Fix typo
delucis 650e14c
Update virtual module types to include `next()`
delucis a743cee
deprecated > not required
delucis ca6f249
More extensive comment
delucis 47988d4
Move route data generation to route component
delucis 50bec23
Add tests for starlightRoute getter
delucis 765beca
Handle route data for SSR route
delucis 0de3e36
Fix missing `next` type in middleware handler type
delucis ca58aed
Add `order: default` to `addRouteMiddleware` types in docs and tweak …
delucis 9ad43c1
Merge branch 'main' into chris/locals-route-data
delucis 13d2038
Switch to alternative SSR approach more close to what we already did
delucis 883d4a5
Add doc comments to `locals` types
delucis 56621a6
Merge branch 'main' into chris/locals-route-data
delucis 5471975
Improve `starlightRoute` error message
delucis 102bd61
Fix test snapshot of error message
delucis 43b67a3
`this` > `that`
delucis 6d05cd9
Docs pass
delucis 8a41943
Remove demo middleware from docs
delucis 9f3475a
Fix broken links
delucis bd785d9
Update Docsearch plugin peer dependency version
delucis 8238a86
Add DocSearch plugin changeset
delucis 1820dec
Merge branch 'main' into chris/locals-route-data
delucis 1ffda84
Fix type that got lost in the merge
delucis 0496d99
Removes DocSearch plugin changeset as one was already added to the sa…
delucis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 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> | ||
| )} | ||
| ``` | ||
|
|
||
| Any plugins you use that add component overrides will also need to be updated at the same time as updating Starlight. | ||
delucis marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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`: | ||
|
|
@@ -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> | ||
|
|
@@ -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> | ||
| ) | ||
|
|
||
delucis marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| --- | ||
| 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. | ||
delucis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| <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> | ||
|
|
||
| #### 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 + '!'; | ||
| }); | ||
| ``` | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.