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.

Swagger UI to Scalar migration Validate the spec, build with the Scalar CLI, embed Scalar, then redirect legacy Swagger UI URLs to the new docs. Swagger UI legacy validate spectral lint scalar build embed Scalar at /docs 301 from legacy

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-ui class 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.com at the API gateway, or proxy test calls through a same-origin endpoint.
  • @scalar/cli build fails on missing peers. The CLI needs its peer dependencies installed. Run npm ci before scalar build so 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.