Scalar
Embed Scalar's self-contained API reference UI on a single route, with any Scalar option passed straight through.
Blume’s own renderer is what openapi(), asyncapi(), and graphql() give you: one real page per operation, in your sidebar, search, and llms.txt, with a Try it playground. If you’d rather embed Scalar’s self-contained API reference UI — its own sidebar, search, theme, and request client on a single route — list a scalar() adapter from blume/reference instead. It takes an OpenAPI or AsyncAPI document and Scalar detects which:
import { defineConfig } from "blume";
import { scalar } from "blume/reference";
export default defineConfig({
reference: [
scalar({
spec: "./openapi.yaml",
theme: "purple", // a Scalar theme name
}),
],
});
That mounts the embed at /reference. spec is either an http(s) URL, which the page loads from the browser, or a path to a local file, which is read at build time and inlined so the page stays self-contained. route moves it, and sources publishes several documents, each on its own route — the same label/route rules the native adapters follow:
reference: [
scalar({
route: "/api",
sources: [
{ label: "Public API", spec: "./public.json" }, // → /api/public-api
{ label: "Legacy API", route: "/legacy", spec: "./legacy.json", noindex: true },
],
}),
],
A Scalar embed and native pages can sit side by side in the list — an openapi() adapter for the current API and a scalar() adapter for a legacy one, say — as long as their routes differ. Like every reference, the embed doesn’t add a header tab on its own; point a navigation tab at its route to surface it.
What the embed doesn’t do
A Scalar-rendered reference is a self-contained page on its own route. It doesn’t weave into Blume’s sidebar, search, or llms.txt, so of the native adapters’ per-source controls only noindex applies here (it adds crawler noindex metadata and keeps the page out of the sitemap); there is no codeSamples, expandSchemas, or playground to set. Scalar brings its own request client, which calls your target API directly from the browser (Blume’s playground.proxy route isn’t available here), so the API must allow cross-origin requests from the docs site (Access-Control-Allow-Origin).
The embed does follow Blume’s light/dark toggle: it’s pinned to the page’s theme when it mounts and switches with it, so Scalar’s own theme switch is hidden (pass forceDarkModeState or darkMode to hand color mode back to Scalar). Without a theme, Blume layers its accent and radius onto Scalar’s default theme; a named theme replaces that. The adapter declares @scalar/astro as its runtime dependency, so the generated project only lists it when a scalar() adapter is configured.
Passing Scalar options
theme is the one option most people reach for, but Scalar supports many more. Every key you pass to scalar() beyond spec, sources, route, and theme is forwarded verbatim as Scalar configuration to the embedded reference — Blume doesn’t gate the keys, so anything Scalar accepts flows through (JSON values only, since the configuration is inlined into the generated page):
reference: [
scalar({
spec: "./openapi.yaml",
localization: { locale: "es" }, // translate Scalar's own UI
agent: { disabled: true }, // disable the Scalar Agent
hideTestRequestButton: true,
orderSchemaPropertiesBy: "preserve",
}),
],
Blume’s own i18n translates the docs chrome, but Scalar has a separate localization system — set localization.locale to translate the embedded reference too. Forwarded options win over Blume’s derived config, so anything set here (including customCss or the spec content/url) overrides Blume’s defaults. The one key that can’t be forwarded is Scalar’s own multi-document sources: that name is Blume’s, and each Blume source becomes its own page.
AsyncAPI documents
Point spec at an AsyncAPI document and the embed renders channels, operations, messages, and a Models section. Scalar has no AsyncAPI playground of its own, so choosing the embed over asyncapi() trades Blume’s event composer away. A GraphQL schema has no Scalar embed at all — the graphql() reference is always rendered natively.