Scalar & Modern Docs Integration
Scalar is a modern OpenAPI reference renderer built around a first-class HTTP client: developers read an endpoint and execute it from the same page, with credential inputs derived from your spec. It ships dark mode, a fast client-side search, and a self-contained static build with no server runtime. This guide is part of Developer Portal Frameworks & UI Setup, and it walks through configuring Scalar via the @scalar/api-reference package, theming it from your design tokens, wiring a CI build, and routing multiple API versions cleanly.
Scalar suits teams who want an interactive “try it” experience without standing up a backend. Where a static renderer like Redoc produces a read-only reference, Scalar puts a request runner inline — useful for onboarding, support, and exploratory testing. If you are weighing the two renderers, the Redoc vs Scalar comparison breaks down the trade-offs, and teams arriving from the classic Swagger UI should read Migrating from Swagger to Scalar for better DX for a step-by-step path.
Prerequisites & Environment Setup
You need Node.js 18 or newer, an OpenAPI 3.0 or 3.1 description, and a static host (Vercel, Netlify, S3 + CloudFront, or an internal cluster). Scalar offers two integration surfaces: a CDN-delivered standalone script for plain HTML pages, and the @scalar/api-reference package for embedding inside a JavaScript app. Choose based on where the reference lives — a standalone marketing-style docs page favors the script tag; a React or Vue portal favors the package.
Pin your versions. Scalar releases frequently, and an unpinned dependency can shift layout or tokens between builds. For the package approach, install it as a regular dependency and commit the lockfile:
npm install @scalar/api-reference@latest
npm ls @scalar/api-reference # record the resolved version
For the CDN approach, pin a specific version in the script URL rather than using a floating @latest tag in production:
<script src="https://cdn.jsdelivr.net/npm/@scalar/[email protected]/dist/browser/standalone.js"></script>
Before any build, make sure your spec is valid. Scalar will render a malformed spec into broken or incomplete docs rather than erroring loudly, so run a linter first. @redocly/cli works well as a pre-build gate — see Redocly & OpenAPI UI Configuration for its rule set — or use Spectral as covered in Spec Linting & Governance.
Core Configuration
The standalone script tag reads its settings from a data-configuration attribute containing a JSON object. The minimal version points at a spec and turns on dark mode:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>API Reference</title>
<link rel="stylesheet" href="/scalar-theme.css" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/@scalar/[email protected]/dist/browser/standalone.js"></script>
<script>
Scalar.createApiReference('#app', {
url: '/openapi.yaml', // spec location, fetched at load
theme: 'default', // built-in theme name
darkMode: true, // start in dark mode
hideModels: false, // show the Models section
hiddenClients: ['unirest'], // remove noisy client snippets
defaultHttpClient: { // pre-select a code-sample language
targetKey: 'shell',
clientKey: 'curl'
}
})
</script>
</body>
</html>
For an app, import the same factory and configure it in code. This is the path to use inside a React or Vue portal so the reference shares the host bundle and routing:
// reference.js — bundled with your portal
import { createApiReference } from '@scalar/api-reference'
import '@scalar/api-reference/style.css'
createApiReference('#app', {
url: '/openapi.yaml',
darkMode: true,
// metaData drives <title>/<meta> for SEO when server-rendered
metaData: { title: 'Payments API Reference' },
// authentication can be pre-filled from the spec's securitySchemes
authentication: { preferredSecurityScheme: 'bearerAuth' },
})
The two configuration keys teams most often miss are defaultHttpClient, which sets the code-sample language shown by default, and hiddenClients, which trims the client list to the languages you actually support. Authentication needs no special wiring beyond declaring securitySchemes in the spec — Scalar renders the matching inputs and feeds them to the built-in HTTP client, keeping credentials in the browser.
Integration Pattern
Build Scalar’s static output in CI, gating on a lint pass so a broken spec never ships. The workflow below validates, builds, and deploys; it triggers only when the spec changes:
# .github/workflows/scalar-build.yml
name: Build Scalar Docs
on:
push:
branches: [main]
paths: ['openapi.yaml']
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Lint spec (gate before build)
run: npx @redocly/cli lint openapi.yaml --format=github
- name: Build Scalar static docs
run: npx @scalar/cli document bundle openapi.yaml --output dist/openapi.json
- name: Assemble static site
run: npm run build:docs # copies index.html + pinned standalone.js + dist/openapi.json
- name: Deploy
run: npx vercel deploy dist/ --prod --token "$VERCEL_TOKEN"
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
The lint step uses @redocly/cli because it returns a non-zero exit on error-severity violations, which fails the job cleanly. The @scalar/cli document bundle step resolves all $refs into a single openapi.json that the page loads with no further network round-trips. Keep the pinned standalone.js in your repo or vendored assets so the deployed page never silently pulls a newer renderer.
Advanced Options
Theme from design tokens, not hardcoded values. Override Scalar’s CSS custom properties in an external stylesheet and map them to your design system, so one token file drives both the portal shell and the reference:
/* scalar-theme.css */
.scalar-app {
--scalar-font: 'Inter', sans-serif;
--scalar-color-1: #16295d; /* primary text */
--scalar-color-2: #334155; /* secondary text */
--scalar-background-1: #ffffff;
}
[data-theme='dark'] .scalar-app {
--scalar-color-1: #e2e8f0;
--scalar-background-1: #0f172a;
}
Use a CSS @layer to isolate these rules below a framework reset; otherwise Tailwind’s preflight or Bootstrap’s resets can override Scalar’s scoped variables. The shared light/dark token approach is detailed in Multi-Theme & Dark Mode Support.
Version routing. Build one output directory per version and serve each from a distinct path, then add a landing page that links to all of them:
npx @scalar/cli document bundle openapi-v1.yaml --output dist/v1/openapi.json
npx @scalar/cli document bundle openapi-v2.yaml --output dist/v2/openapi.json
For deprecated versions, keep the static bundle live but add a visible deprecation banner and a server-side redirect to the current path once the grace period in your API lifecycle policy expires.
Embed inside a component tree. In a React or Vue portal, render the reference as a component via @scalar/api-reference instead of an iframe, so it shares routing, theme toggling, and the host bundle. This is the cleaner path when the reference is one tab among many in a larger developer portal.
Verification & Testing
Run the build locally before pushing and confirm each artifact exists and is non-empty:
npx @redocly/cli lint openapi.yaml
echo "lint exit: $?" # 0 = pass; 1 = errors block the build
npx @scalar/cli document bundle openapi.yaml --output dist/openapi.json
test -s dist/openapi.json && echo "bundle OK"
Serve the assembled directory and load it in a browser to confirm three things: the reference renders, dark mode toggles, and the built-in HTTP client can execute a request against a test server. A quick static check confirms the page references your bundled spec rather than a remote one:
npx http-server dist -p 8080 &
curl -s http://localhost:8080/ | grep -q "openapi.json" \
&& echo "page references bundled spec" || echo "spec reference MISSING"
After deploy, fetch the live page and grep for your API title to confirm the deploy replaced the previous build rather than failing silently.
Troubleshooting
Blank reference, spinner never resolves. The page can fetch standalone.js but cannot fetch the spec, usually a CORS failure or a wrong url. Bundle the spec into the same origin as the page (the @scalar/cli document bundle step does this) so there is no cross-origin fetch, and confirm the url path resolves with curl.
Spec loads but operations are missing. The spec is OpenAPI 2.0 or has invalid $refs that Scalar skipped silently. Convert 2.0 with npx swagger2openapi swagger.yaml -o openapi.yaml, then re-run the linter to catch unresolved references before building.
Theme overrides have no effect. Your CSS framework’s reset is winning the cascade over Scalar’s scoped variables. Wrap Scalar’s overrides in @layer placed after the framework reset, and target .scalar-app rather than deep internal selectors that are not a stable API.
Layout regression after a deploy. The page pulled a newer renderer because the script tag used a floating @latest. Pin the version in the script URL (or vendor the file) and treat renderer upgrades as reviewed changes with a visual diff.
FAQ
Can Scalar render OpenAPI 2.0 (Swagger) specs directly?
No. Scalar requires OpenAPI 3.0 or 3.1. Convert legacy Swagger 2.0 with npx swagger2openapi swagger.yaml -o openapi.yaml, then point Scalar at the converted file.
How does Scalar handle authentication in the reference UI?
Scalar reads securitySchemes from your OpenAPI spec and renders the matching credential inputs for API keys, HTTP Basic, and OAuth2 flows. Credentials entered in the UI stay in the browser and drive the built-in HTTP client; they are never persisted server-side.
Do I configure Scalar through a script tag or a package?
Both are supported. Use the data-configuration attribute on the standalone script tag for a static page, or import createApiReference from @scalar/api-reference to embed and configure it inside a React or Vue app.
Is self-hosting required for data residency?
Deploy the static build output to your own CDN or internal cluster to keep everything in your network. The build has no external runtime dependency once the assets are in place, so it satisfies typical data-residency requirements.
Related
- Developer Portal Frameworks & UI Setup — parent overview of renderer and portal choices.
- Migrating from Swagger to Scalar for better DX — step-by-step migration path.
- Redoc vs Scalar comparison — choosing between the two renderers.
- Redocly & OpenAPI UI Configuration — the static-generation alternative.
- Swagger UI Customization — the renderer many teams migrate away from.