Migrating from Swagger UI to Scalar for Better DX
Swapping Swagger UI for Scalar is rarely just a script-tag change — there is custom CSS to audit, a spec to validate, a CI step to replace, and legacy URLs that other teams have bookmarked. This guide is part of Scalar & Modern Docs Integration and the broader Developer Portal Frameworks & UI Setup section. It lays out an ordered migration: validate, build, embed, redirect, and cut over without breaking downstream imports.
The migration is worth doing when Swagger UI’s interactive console and theming have become a maintenance burden, or when you want the cleaner request client and scoped theming Scalar provides — but only once the spec is genuinely OpenAPI 3.x, since Scalar rejects 2.0 outright.
Problem & Context
The before-state: Swagger UI is wired up with a pile of .swagger-ui-scoped CSS overrides, a build step that copies swagger-ui-dist assets, and a spec that may be Swagger 2.0 in disguise — Swagger UI renders malformed 2.0 specs quietly, so nobody noticed. Other teams have hard-coded links to /swagger-ui.html and /api-docs in runbooks and CI dashboards. A naive swap to Scalar breaks all of that: the CSS does nothing because Scalar uses scoped CSS variables, the build fails on an unconverted 2.0 spec, and the old URLs 404.
A clean migration validates first, swaps the build, embeds Scalar, and only then retires the legacy routes with redirects rather than deletions.
Step-by-Step Solution
1. Validate and audit the spec
Scalar requires OpenAPI 3.0.3 or 3.1.0. Install the validators and run them:
npm install -g @apidevtools/swagger-parser @stoplight/spectral-cli
npx swagger-parser validate openapi.yaml
npx spectral lint openapi.yaml --ruleset .spectral.yaml
Expected output for a clean spec:
openapi.yaml is valid
0 errors, 0 warnings
If the spec is Swagger 2.0, convert it, then review by hand:
npx swagger2openapi swagger.yaml --outfile openapi.yaml
swagger2openapi does not upgrade x- extensions or move securityDefinitions into components.securitySchemes — fix those manually before continuing.
2. Swap the CI build step
Replace the Swagger UI asset copy with the Scalar CLI. Note that @scalar/cli has no validate subcommand, so keep Spectral as the gate:
# .github/workflows/docs.yml
name: Build API Docs
on:
push:
paths: ['openapi.yaml']
jobs:
validate-and-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: Validate spec
run: npx @stoplight/spectral-cli lint openapi.yaml --ruleset .spectral.yaml
- name: Build Scalar docs
run: npx @scalar/cli build --input openapi.yaml --output ./dist/docs
3. Embed Scalar
Install the reference component and mount it. Pin the version in package.json, not in the import path:
npm install @scalar/api-reference
// App.vue (Vue 3)
import { ApiReference } from '@scalar/api-reference'
export default {
components: { ApiReference },
template: `
<ApiReference
:configuration="{
spec: { url: '/openapi.yaml' },
theme: 'default',
darkMode: false,
hideDownloadButton: false
}"
/>
`
}
For a no-framework deployment, use the CDN script tag instead:
<script
id="api-reference"
data-url="/openapi.yaml"
src="https://cdn.jsdelivr.net/npm/@scalar/api-reference/dist/browser/standalone.js"
></script>
4. Redirect legacy URLs
Point old Swagger UI paths at the new docs with 301s, and return 410 for routes that no longer exist:
# nginx.conf
location = /swagger-ui.html { return 301 /docs; }
location = /api-docs { return 301 /docs; }
location /swagger-resources { return 410; }
5. Cut over and purge caches
After deploying, purge CDN edge caches so readers do not hit stale routing. For Cloudflare:
curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
Watch 404 rates in analytics for 48 hours to catch any redirect gaps.
Complete Working Example
A single standalone index.html that serves Scalar from the CDN with dark mode and a custom accent, ready to drop behind your web server while you migrate:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Acme API Reference</title>
<style>
/* Scalar uses scoped CSS variables, not .swagger-ui selectors */
:root {
--scalar-color-accent: #0052cc;
--scalar-font: 'Inter', system-ui, sans-serif;
}
body { margin: 0; }
</style>
</head>
<body>
<script
id="api-reference"
data-url="/openapi.yaml"
data-configuration='{"theme":"default","darkMode":true}'
src="https://cdn.jsdelivr.net/npm/@scalar/api-reference/dist/browser/standalone.js"
></script>
</body>
</html>
Pair this with the Nginx redirects above and the Spectral-gated CI build, and the migration is reproducible from a single commit.
Gotchas & Edge Cases
- Custom Swagger UI CSS stops working. Scalar exposes scoped CSS variables such as
--scalar-color-accent, not.swagger-uiclass selectors. Re-express your brand colors and fonts as Scalar variables rather than porting the old stylesheet. - Interactive client returns CORS 403. Scalar runs requests from the browser, so the target server must allow the docs origin. Add
Access-Control-Allow-Origin: https://docs.yourdomain.comat the API gateway, or proxy test calls through a same-origin endpoint. @scalar/clibuild fails on missing peers. The CLI needs its peer dependencies installed. Runnpm cibeforescalar buildso the lockfile tree is present in the runner.
FAQ
Can I run Scalar alongside Swagger UI during migration?
Yes. Mount each on a separate path — for example /docs for Scalar and /docs-legacy for Swagger UI — and route a percentage of traffic with a feature flag or A/B split. That lets you compare developer experience on real usage before retiring Swagger UI for good.
Does Scalar support OpenAPI 2.0 specs?
No. Scalar requires OpenAPI 3.0.3 or 3.1.0 and rejects 2.0 definitions at build time. Convert with swagger2openapi first, then review extensions and security definitions by hand because the converter does not normalize them.
Will migrating break existing Postman or Insomnia imports?
No. Those tools import the raw OpenAPI YAML or JSON, not the rendered UI. As long as the spec URL stays reachable and the document is valid OpenAPI 3.x, imports continue to work exactly as before.