scalar() and swaggerUi(). Each is a separate Payload plugin you add alongside openapi() — mount one, or both on different paths:
payload.config.ts
routes.api, /api by default).
Options
Both renderers accept the same options:
By default the UI loads the spec from the route
openapi() serves, including a custom path, so openapi({ path: '/spec.json' }) needs no change here. The page title is info.title. To keep private docs private, see Limiting who can read the spec.
Pinned versions and SRI
By default each renderer loads one fixed version of its library from jsDelivr:
The page adds an
integrity hash (Subresource Integrity) to these files. If the CDN serves a changed file, the browser does not run it. A plugin release updates the versions and the hashes.
For Scalar, cdnBase is the URL of the script. For Swagger UI, it is the folder that holds swagger-ui-bundle.js and swagger-ui.css.
Self-hosting
To load the UI from your own server, for example with a strict Content Security Policy or without internet access, copy the files to your static assets and setcdnBase:
payload.config.ts
cdnBase loads without an integrity hash, because the plugin cannot know the file. You pick the version.
Passing UI configuration
configuration is passed straight through to the underlying library — Scalar’s createApiReference config or Swagger UI’s SwaggerUIBundle options. It must be JSON-serializable; functions aren’t supported here.
payload.config.ts
Serving a static spec
The UI loads the spec from a URL, and that URL doesn’t have to be the plugin’s runtime endpoint. PointspecURL at a pre-generated file and the UI renders it directly:
payload.config.ts
Language switcher
For multi-language docs, point Scalar’ssources at the spec endpoint with different ?lang= values. When sources is set, the renderer’s own specURL is ignored. Swagger UI has no built-in switcher — mount one instance per language instead. See Internationalization for both setups.