Skip to content

Writing TypeScript Pages

Most pages in this documentation site are written in Markdown (.md) or MDX (.mdx) — you write prose, and Starlight handles the layout, sidebar, table of contents, and theming automatically.

But sometimes you need more control — a custom layout, interactive widgets, or generated content. For those cases, you can write pages in TypeScript using Astro’s .astro component format, while still getting the full Starlight shell (header, sidebar, search, etc.).

  • Interactive demos — embed a live code editor, playground, or widget
  • Dynamic content — generate tables or lists from data at build time
  • Custom layouts — pages that don’t fit the standard prose format
  • Computed documentation — API docs generated from source code
  1. Create an .astro file in src/pages/

    Astro pages in src/pages/ get file-based routing just like content docs. For example, src/pages/my-custom-page.astro becomes /my-custom-page/.

  2. Import the StarlightPage layout component

    This gives your page the full Starlight shell — sidebar, header, search, table of contents, and consistent theming.

  3. Set frontmatter via the frontmatter prop

    Pass title, description, and other metadata the same way you would in Markdown frontmatter.

Create a file at src/pages/docs/my-page.astro:

src/pages/docs/my-page.astro
---
// Summary: A custom TypeScript-authored documentation page that renders
// inside the Starlight shell with full sidebar and theming support.
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
---
<StarlightPage
frontmatter={{
title: 'My Custom Page',
description: 'A page authored in TypeScript/Astro.',
}}
>
<h2>Hello from TypeScript!</h2>
<p>
This page is written as an <code>.astro</code> component, but it looks
and feels exactly like a Markdown page because it uses the
<code>StarlightPage</code> wrapper.
</p>
</StarlightPage>

Visit it at: http://localhost:4321/docs/my-page/

For client-side interactivity, use Astro’s island architecture. You can write components in React, Preact, Svelte, Vue, or Solid — and they hydrate only where needed.

Terminal window
bunx astro add react
src/components/Counter.tsx
// Summary: A simple interactive React counter component demonstrating
// client-side hydration in a Starlight documentation page.
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div style={{ padding: '1rem', border: '1px solid var(--sl-color-gray-5)', borderRadius: '0.5rem' }}>
<p>Count: <strong>{count}</strong></p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={() => setCount(0)} style={{ marginLeft: '0.5rem' }}>Reset</button>
</div>
);
}
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import Counter from '../../components/Counter';
---
<StarlightPage frontmatter={{ title: 'Interactive Demo' }}>
<h2>Live Counter</h2>
<p>This counter is a React component hydrated on the client:</p>
<Counter client:load />
</StarlightPage>

The client:load directive tells Astro to hydrate this component on page load. Other options:

Directive When It Hydrates
client:load Immediately on page load
client:idle When the browser is idle
client:visible When the component scrolls into view
client:media When a CSS media query matches

The script block at the top of every .astro file (between the --- fences) runs at build time and is full TypeScript:

---
// This is TypeScript — runs at build time
interface Person {
name: string;
role: string;
}
const team: Person[] = [
{ name: 'Alice', role: 'Lead' },
{ name: 'Bob', role: 'Contributor' },
];
---
<ul>
{team.map((p) => (
<li><strong>{p.name}</strong>{p.role}</li>
))}
</ul>
src/
├── content/
│ └── docs/ # Markdown / MDX pages (auto-routed by Starlight)
│ ├── getting-started.mdx
│ └── language/
│ └── syntax.md
├── pages/
│ └── docs/ # TypeScript / Astro pages (manual routing)
│ └── my-page.astro
└── components/ # Reusable components (Astro, React, etc.)
└── Counter.tsx
  • Consistent theming: Always wrap custom pages in <StarlightPage> to get the standard header, sidebar, and styles.
  • SEO: Pass title and description in the frontmatter prop.
  • Table of contents: Starlight auto-generates the TOC from <h2> and <h3> elements inside <StarlightPage> — use semantic headings.
  • Sidebar: To add your custom page to the sidebar, include it explicitly in astro.config.mjs under the appropriate section’s items array.