Astro

Create an Astro documentation site with Heyo Docs, or integrate the smallest static setup into an existing project.

For a new documentation site, start with create-heyo-docs. The Astro template connects the content pipeline to Astro, generates every known documentation path at build time, hydrates the documentation UI as a React island, and keeps the optional API request proxy separate from static content.

Use the creator with the Astro template. Remove the flags if you would rather pick the template, deployment, theme, and package manager in the wizard.

bun create @heyo-sh/heyo-docs my-docs --template astrocd my-docsbun run dev

The generated site is a standalone documentation project rather than an installation inside another Astro app. Its content/ directory contains the source MDX, while heyo-docs.config.ts defines navigation, branding, metadata, and the active theme.

Manual, minimal installation

Use the following integration when docs belong inside an existing Astro site. It is deliberately the smallest useful setup: static MDX routes and the documentation UI. Use the generated project when you also need the theme switcher, icon mapping, resource routes, OpenAPI reference pages, or the server-side Try it proxy.

1. Install the integration

This assumes that the existing project uses Astro with the React integration and Tailwind v4. Add the missing packages if it does not.

bun add @heyo-sh/heyo-docs @astrojs/react @fontsource-variable/figtree shadcn tw-animate-cssbun add -d @tailwindcss/vite tailwindcss

2. Add configuration and content

Create heyo-docs.config.ts at the project root:

heyo-docs.config.ts
import { heyoDocs } from "@heyo-sh/heyo-docs";

export default heyoDocs({
  title: "Acme Docs",
  description: "Documentation for Acme.",
  content: "content",
  theme: "grain",
  groups: [
    {
      group: "Documentation",
      sections: [{ section: "Get started", pages: ["index"] }],
    },
  ],
  branding: { name: "Acme" },
});

Add content/index.mdx:

mdx
---title: Welcomedescription: Start here.---# WelcomeYour documentation is ready.

3. Connect Astro and the theme

heyoDocsAstro registers the Vite-powered MDX/content pipeline with Astro. Setting output: "static" tells Astro to emit the documentation paths during the build.

astro.config.ts
import { defineConfig } from "astro/config";
import react from "@astrojs/react";
import tailwindcss from "@tailwindcss/vite";
import { heyoDocsAstro } from "@heyo-sh/heyo-docs/astro";

import config from "./heyo-docs.config";

export default defineConfig({
  integrations: [react(), heyoDocsAstro({ config })],
  output: "static",
  vite: { plugins: [tailwindcss()] },
});
src/styles/app.css
@import "@fontsource-variable/figtree";@import "@heyo-sh/heyo-docs/theme/grain.css";:root {  --heyo-docs-font-family: "Figtree Variable", sans-serif;}

4. Add a static catch-all page

Create a small React island for the documentation UI. This minimal version uses regular links; the generated template adds client-side navigation, icons, and a persisted colour-mode control.

src/components/docs-app.tsx
import { DocsApp } from "@heyo-sh/heyo-docs";

import config from "../../heyo-docs.config";
import { pages } from "virtual:heyo-docs-content";

export function AstroDocsApp({ pathname }: { pathname: string }) {
  return <DocsApp config={config} pages={pages} pathname={pathname} />;
}

Then render it from src/pages/[...slug].astro. getStaticPaths() keeps the route list in sync with the same pages used in the sidebar.

astro
---import { AstroDocsApp } from "../components/docs-app";import "../styles/app.css";import { pages } from "virtual:heyo-docs-content";export function getStaticPaths() {  return pages.map(({ slug }) => ({    params: { slug: slug === "/" ? undefined : slug.slice(1) },  }));}const pathname = Astro.params.slug ? `/${Astro.params.slug}` : "/";---<AstroDocsApp client:load pathname={pathname} />

Run bun run dev and open /. For a hybrid deployment with the optional request proxy, or for generated OpenAPI pages, use the complete Astro template from create-heyo-docs; its adapter and resource routes handle that boundary.

Development feedback

Saving an MDX file while bun run dev is running refreshes the page without a server restart. output: "static" controls astro build, not local development; deployed content changes still require a fresh build and deploy.

Rendering strategy for the Astro template

This rendering strategy applies to the Astro template generated by create-heyo-docs.

The Astro template treats documentation as a release artefact. MDX pages, OpenAPI navigation, endpoint payloads, Markdown mirrors, and discovery files are produced during astro build and can be served from a CDN. A deployed server exists only for the optional Try it proxy; it is not involved in reading or rendering documentation content.

flowchart LR
A[MDX and OpenAPI sources] --> B[astro build]
B --> C[Static HTML and assets]
B --> D[Endpoint JSON shards]
C --> E[CDN]
D --> E
F[Try it POST] --> G[Astro adapter function or Worker]
G --> H[Declared API server]

Why this model

Documentation changes are published through a build and deployment. Rendering the same MDX and OpenAPI model again for every reader would add latency and runtime cost without making the documentation fresher. Static output instead provides predictable cacheability, low time to first byte, and resilience when the application runtime is unavailable.

The template configures Astro with output: "static". Its catch-all documentation and Markdown routes export getStaticPaths(), using the build-generated MDX page list and OpenAPI endpoint index. Every known path is therefore emitted during the build, including generated API-reference pages.

OpenAPI data delivery

The browser receives a compact endpoint index for sidebar navigation and search. Detailed content is split into static JSON files below:

text
/_heyo-docs/openapi/<group>/<tag>/<operation>.json

For each prerendered API route, Astro also serialises that route’s detailed payload into the static island input. The server-rendered HTML and the hydrated React component therefore begin with the same complete endpoint data: opening an API page does not first show a compact version and then replace it with a richer UI. There is no endpoint-detail request or layout shift on the initial page load.

The JSON shard remains a public, cacheable static artefact for integrations and future prefetching. Each payload contains the endpoint’s request, response, examples, security metadata, and only the component schemas reachable from that endpoint. It intentionally does not duplicate the complete OpenAPI document in the shared browser bundle.

The one dynamic route

POST /heyo-docs-internal/openapi-request remains on demand and is marked with export const prerender = false. It is used only when a reader selects Send request. The handler validates that the selected server is declared by the OpenAPI document, then performs the request server-side. This avoids browser CORS limitations and keeps the documentation site from becoming an unrestricted open proxy.

Astro still needs a deployment adapter for this route. The generated project uses the Node adapter by default; the Cloudflare and Vercel deployment overlays replace it with their respective adapter. Static pages and endpoint JSON remain cacheable assets on all three targets.

Operational implications

  • Editing MDX or an OpenAPI schema requires a build and redeploy.
  • Remote schemas are fetched during the build, so pin their URL or commit for reproducible releases.
  • Build time and output size grow with the number of generated API routes. This is deliberate: every route is available as static content immediately after deployment.
  • The Try it proxy can be removed if interactive requests are not required; the rest of the documentation remains fully static.

What is not dynamic

Theme selection, local search, sidebar navigation, and endpoint rendering are browser interactions backed by static page data and assets. They do not require Astro SSR. The adapter function is reserved for actions that genuinely need a server: currently the optional API request proxy.

Next steps