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