Add llms.txt to your Astro Starlight site
I added llms.txt to a Starlight docs site the other day, and it was easier than I thought it was gonna be, which is often the case with Astro.
This works with every astro site, whether it’s Starlight or not. Here’s how you do it.
(It’s a proposal for a standardised way to help LLMs navigate websites).
The llms.txt endpoint
Create src/pages/llms.txt.ts, which will create the file at the root of your website.
In Starlight, all of your docs are in the docs content collection, which makes this process so easy. It also gives you this nice separation between the docs and other pages. In a regular astro site, just give it the collection (or collections) that you want to include for the LLM.
import type { APIRoute } from 'astro';
import { getCollection } from 'astro:content';
const siteUrl = 'https://your-site.com';
export const GET: APIRoute = async () => {
const docs = await getCollection('docs');
const docLines = docs.map((doc) => {
const title = doc.data.title;
const description = doc.data.description || '';
const url = `${siteUrl}/${doc.id}.md`;
return description ? `- [${title}](${url}): ${description}` : `- [${title}](${url})`;
});
const content = `# Your Site Name
> A short description of what your site is about.
This is the documentation site for Your Project at ${siteUrl}
## Docs
${docLines.join('\n')}
`;
return new Response(content, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
};
Exposing docs as raw markdown
Now the llms.txt links to .md versions of your docs. We need to create those endpoints.
Create src/pages/[...slug].md.ts:
import type { APIRoute, GetStaticPaths } from 'astro';
import { getCollection } from 'astro:content';
export const getStaticPaths: GetStaticPaths = async () => {
const docs = await getCollection('docs');
return docs.map((entry) => {
return {
params: { slug: entry.id },
props: { entry },
};
});
};
export const GET: APIRoute = ({ props }) => {
const { entry } = props;
const separator = '---';
const content = [
separator,
`title: ${entry.data.title}`,
separator,
'',
entry.body,
].join('\n');
return new Response(content, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
};
This creates a .md route for every doc in your collection. The markdown includes the frontmatter with the title, plus the raw body content.
That’s it. Visit /llms.txt to see the index, and any doc at /path/to/doc.md to get the raw markdown.