How to fully autogenerate Starlight sidebars (without losing control)

Have you ever wanted to simplify sidebar generation in your Starlight project? Have you tried autogenerating the entire sidebar only to find it doesn’t let you customise the structure to your needs?
This post demonstrates two Starlight features that make fully autogenerated sidebars flexible and reduce maintenance.
In medium to large Starlight projects, the problem arises that the manual creation and maintenance of the Starlight sidebar configuration is a tedious and time-consuming task.
In the past, Starlight didn’t provide the right primitives for this, and the recommended approach was to move the autogeneration of the sidebar configuration one level deeper - so that only folders without nested subfolders were autogenerated. However, with the introduction of Route Data and the more recent generateId()
hook of docsLoader
, these scenarios can now be solved much more easily and efficiently.
A common problem always was: “How can I customise the order of sidebar folders if I autogenerate the sidebar?” Old issues like #1223 suggest a solution inspired by Nuxt Content where each segment of the whole route can be prefixed with numbers + dots to determine numeric ordering on a file-based level. An example for such a project could look like this:
Directorysrc/content/docs/nested-folder
Directory1.frameworks
- 1.vue.md
- 2.nuxt.md
Directory2.examples
- 1.vercel.md
- 2.netlify.md
If you want to use this approach in Starlight, it doesn’t work out-of-the-box, but with a simple setup you can make it work.
Starlight 0.35.0 introduced a callback function which you can pass to docsLoader()
that can manipulate the id (URL) of the generated page. By splitting the entry by "/"
and removing any number + dot on the segment level, you have the same functionality now in Starlight:
import { defineCollection } from "astro:content";import { docsLoader } from "@astrojs/starlight/loaders";import { docsSchema } from "@astrojs/starlight/schema";
const leadingNumberAndDotRegEx = /^\d+\./;const fileExtensionRegEx = /\.(md|mdx)$/;
export const collections = { docs: defineCollection({ loader: docsLoader({ // Remove file extension and // leading numbers + dot from each segment generateId: ({ entry }) => entry .replace(fileExtensionRegEx, "") .split("/") .map((segment) => segment.replace(leadingNumberAndDotRegEx, "")) .join("/"), }), schema: docsSchema(), }),};
The URL to access the pages now changes e.g., from /nested-folder/1.frameworks/1.vue
to /nested-folder/frameworks/vue
.
However, the folder names in the sidebar still include the 1.
prefixes. So let’s solve the sidebar manipulation as well:
Starlight 0.32.0 introduced Route Data, which we can now utilize to manipulate folder names in the sidebar to
- remove any leading number + dot from the folder name and
- apply “Title Case” so they don’t look completely lowercase
import { defineRouteMiddleware } from "@astrojs/starlight/route-data";
const leadingNumberAndDotRegEx = /^\d+\./;const wordSplitterRegEx = /\w\S*/g;
function toTitleCase(str: string) { return str.replace(wordSplitterRegEx, (word) => { return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); });}
function cleanGroupLabels(entries: any[]) { for (const entry of entries) { if (entry.type === "group") { // Remove leading number + dot let label = entry.label.replace(leadingNumberAndDotRegEx, ""); // Convert to Title Case entry.label = toTitleCase(label); // Recurse into children cleanGroupLabels(entry.entries); } }}
export const onRequest = defineRouteMiddleware((context) => { const { sidebar } = context.locals.starlightRoute; cleanGroupLabels(sidebar);});
Note that internationalisation (i18n) of folder names is a missing feature with such a setup as there is currently nowhere to define translations of folder names, they just get converted into title case. Implementing logic to dynamically load translations could involve reading the locale
field from the route data and define translations in json
files. Otherwise, use HiDeoo’s plugin, which supports i18n.
Don’t forget to add the routeData.ts
file to your astro.config.mjs
:
starlight({ title: "Autogenerate whole sidebar", social: [{ icon: "github", label: "GitHub", href: "https://github.com/withastro/starlight" }], sidebar: [ { label: "Root Folder", autogenerate: { directory: "nested-folder" }, }, ], routeMiddleware: "./src/routeData.ts", }),
Of course you can adjust the code to your needs (e.g., remove the “Title Case” transformation because your file names are already written correctly).
If you want to autogenerate your whole sidebar and still have the flexibility to customise everything you want, HiDeoo also made a dedicated plugin for such use cases: Starlight Auto Sidebar.
Especially those two features got mentioned in this article:
- Controlling the order of individual directories
- Adapting the displayed label of individual folders
However, if you want to automate the whole generation without having to specify labels and orders manually for each directory, coding the solution yourself (with some guidelines in this blog) is the preferred and recommended solution.
If you want to check out the code presented in this blog, feel free to visit the StackBlitz or the source code in the GitHub repo.