Skip to main content

Astro / Starlight

Embed OmniSpec in an Astro site or a Starlight docs site. Astro is framework-agnostic, so you have two paths: the framework-agnostic Web Component (no React needed), or a React island using the React component (needs the React integration). Both work — pick by whether you already ship React on the page.

When to use​

Use this guide for an Astro or Starlight site. Astro statically generates pages and the renderer is client-only, so in both paths it must be mounted client-only — use client:only (not client:load), which skips server rendering of the renderer entirely rather than rendering it on the server and hydrating.

Option A — Web Component (no React)​

The simplest path: no framework integration, no build wiring. Drop the standalone bundle and the element into an .mdx page.

---
title: API Reference
---

<div style="height: 100vh">
<omnispec-renderer
spec-url="https://petstore3.swagger.io/api/v3/openapi.json"
theme-base="auto"
></omnispec-renderer>
</div>

<script src="https://unpkg.com/@apiboost/omnispec@latest/dist/wc/standalone.js"></script>

Astro leaves <script src> tags with a full URL as-is and runs them in the browser, so the element registers client-side. If you install the package instead of using the CDN, register it from a client script:

<script>
import '@apiboost/omnispec/wc'
</script>

An Astro <script> (without a src to an external URL) is bundled and runs on the client only, which keeps the browser-only registration out of the SSG build.

Option B — React island (client:only)​

If you prefer the React component, add the React integration and mount it as a client-only island.

npx astro add react
npm install @apiboost/omnispec
// src/components/ApiReference.tsx
import {OmniSpecRenderer} from '@apiboost/omnispec'

export default function ApiReference() {
return (
<div style={{height: '100vh'}}>
<OmniSpecRenderer
spec="https://petstore3.swagger.io/api/v3/openapi.json"
theme={{base: 'auto'}}
/>
</div>
)
}
---
title: API Reference
---

import ApiReference from '../../components/ApiReference'

<ApiReference client:only="react" />
info

Use client:only="react", not client:load. client:only skips server rendering of the island, so Astro's static build never evaluates the renderer's browser-only APIs (DOM, fetch, IntersectionObserver).

Theme sync​

Passing an explicit theme base puts the renderer in controlled mode: it follows the value you give it and hides its own theme toggle, so the site's switcher stays the single source of truth.

Starlight tracks the active theme with a data-theme="light" | "dark" attribute on <html>. The simplest option in either path is theme-base="auto" (Web Component) or theme={{base: 'auto'}} (React), which follows the system preference. To follow Starlight's own toggle, mirror data-theme onto the element:

<script>
const el = document.querySelector('omnispec-renderer')
const html = document.documentElement
const sync = () =>
el?.setAttribute('theme-base', html.dataset.theme === 'dark' ? 'dark' : 'light')
sync()
new MutationObserver(sync).observe(html, {
attributes: true,
attributeFilter: ['data-theme'],
})
</script>

Styling and the shadow DOM​

The Web Component uses open shadow DOM: your page CSS does not leak in. Style it by setting --omnispec-* CSS custom properties on the host element — they inherit through the shadow boundary:

omnispec-renderer {
--omnispec-color-primary: #8b5cf6;
}

See Theming for the full token list.

Sizing and the sidebar​

The renderer ships its own sidebar navigation. Give the element a tall container (height: 100vh). Starlight already renders its own doc sidebar, so add layout="stacked" (Web Component) or layout="stacked" prop (React) to drop the renderer's sidebar and avoid a double sidebar.

Next steps​