# Swagger UI's three npm packages, its exports map, and CORS on by default

> Swagger UI renders an OpenAPI description into an interactive documentation page, and the repository publishes three different npm packages plus a Docker image for serving it. The details that bite are in the exports map, the spec compatibility table, and the image's environment defaults.

**swagger-api/swagger-ui** — Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.

- Repository: https://github.com/swagger-api/swagger-ui
- Website: https://swagger.io
- Stars: 29,027 · Forks: 9,255
- Language: JavaScript
- License: Apache-2.0
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/swagger-api-swagger-ui

## Three npm packages, and the one the README steers you away from

The repository publishes three npm modules from one codebase, and choosing wrong is expensive in a different direction each time. swagger-ui is the traditional module for single-page applications that can resolve dependencies through a bundler such as Webpack or Browserify. swagger-ui-dist is dependency-free, for a server-side project or a single-page application that cannot resolve npm module dependencies. swagger-ui-react is the same UI packaged as a React component.

The README goes further than describing them. It strongly suggests swagger-ui over swagger-ui-dist for a single-page application, because swagger-ui-dist is significantly larger. That is a bundle size warning, and it is the reason the dist package exists at all: it ships everything needed to serve the UI without a dependency resolver.

There is a fourth route for people who do not want npm at all. Download the latest release and copy the contents of the /dist folder to your server. The tree keeps dist/ checked in at the top level, along with a separate swagger-ui-dist-package/ directory, so both routes are built from files you can inspect before you serve them.

## The exports map gives Node a different file than the browser

The package manifest resolves to different bundles depending on the condition. The browser import is ./dist/swagger-ui-es-bundle-core.js, the browser require is ./dist/swagger-ui.js, and both the node and the default conditions point at ./dist/swagger-ui-bundle.js for import and ./dist/swagger-ui-es-bundle.js for require. The main field is ./dist/swagger-ui.js and the module field is the es-bundle-core file.

That matters more than it looks. A server-side render, a Node test, or a build tool that resolves with the node condition gets a different file from the one the browser loads, quietly, with no error. If your app behaves differently under test than in the browser, this map is the first place to look.

The exports map also lists individual assets, including ./dist/swagger-ui.css, ./dist/swagger-ui-standalone-preset and ./dist/oauth2-redirect.html. That last one is the document an OAuth2 flow redirects back to, and it is reachable only through the exports map, so a setup that serves the UI from your own server has to serve that file too or the authorization step lands nowhere.

## The compatibility table decides your version, not the major number

The OpenAPI Specification has had five revisions since its first version in 2010, and the table maps each Swagger UI release to the spec versions it covers. Read the rows and the answer is plain. The 5.32.0 row, released 2026-02-27, is the first to include 3.2.0, and it lists 2.0, 3.0.0 through 3.0.4, 3.1.0 through 3.1.2 and 3.2.0. The 5.19.0 row from 2025-02-17 stops at 3.1.2. The 5.0.0 row from 2023-06-12 stops at 3.1.0, the 4.0.0 row from 2021-11-03 stops at 3.0.3, and the 2.x rows reach only 1.1 and 1.2.

So a team whose description uses 3.2.0 has to be on 5.32.0 or newer, and a team that upgrades from 4.x to 5.x may find that spec features they never used stopped being covered.

One catch in the table itself. Its newest row is 5.32.0, while the current release is 5.33.0, so the coverage of the newest version is not stated anywhere in the README. The release cadence makes that a real gap: v5.32.14 shipped on 2026-08-18, v5.32.15 on 2026-09-04, and v5.33.0 on 2026-09-16.

## npm install reports to Scarf unless you set one field

The package collects anonymized installation analytics through Scarf, and the README says the collection only runs during installation. There are two documented ways to stop it, one in your own project and one in the environment.

In package.json:

```json
// package.json
{
  // ...
  "scarfSettings": {
    "enabled": false
  }
  // ...
}
```

Or as an environment variable on the install itself:

```bash
SCARF_ANALYTICS=false npm install
```

The environment variable is the one that matters in practice. A build agent that cannot reach the internet will either fail on the request or silently skip it depending on the registry and the network policy, and neither outcome is something you want discovered during a release. Setting it in the CI environment is one line and removes the question.

## The Docker image ships CORS true and simulates non-root with chmod

The image is nginx:1.31.5-alpine with nodejs and a list of Alpine packages pinned to version floors, libxml2, libexpat, libxslt, xz-libs, c-ares, libpng, zlib, libcrypto3, libssl3, musl, musl-utils and nghttp2-libs. The environment block opens with an API_KEY placeholder and then sets the variables that matter:

```dockerfile
ENV SWAGGER_JSON="/app/swagger.json" \
    PORT="8080" \
    PORT_IPV6="" \
    BASE_URL="/" \
    SWAGGER_JSON_URL="" \
    CORS="true" \
    EMBEDDING="false"
```

CORS is true out of the box, and the UI is built to let anyone interact with your API's resources, including your end consumers. A Swagger UI on a public host with CORS enabled is an invitation, so this is the first variable to change when the page leaves your network. SWAGGER_JSON points at /app/swagger.json inside the container, which is the file you mount your description into, and PORT is 8080 unless you say otherwise.

The unprivileged story is where the Dockerfile admits its own compromise. A comment says it simulates running nginx as non root, that in future they want nginxinc/nginx-unprivileged, and that separate unprivileged images will be tagged later. What ships today is chmod 777 on the nginx directories and cache and a chmod 666 on the root, which is the opposite of what a read-only root filesystem or a strict runtime policy wants.

## Patch numbers accumulate before the minor moves

The recent tags show the shape of the numbering. v5.32.14 on 2026-08-18, v5.32.15 on 2026-09-04, then v5.33.0 on 2026-09-16, with the last push to the repository on 2026-09-28. Fixes land as patch releases on the current minor, and a new minor arrives when there is enough in it, which means a team that pins 5.32.15 and a team that pins 5.33.0 are running code with a different feature set, not a different bugfix level.

The version is written by tooling rather than by hand: the release script is release-it with the configuration in ./release/.release-it.json, and a .releaserc file sits at the root. So the tag and the version field in package.json agree by construction, and a mismatch means something is wrong with the release, not with your pin.

Old majors are not deleted, they are branched. The README points anyone looking for the older version at the 2.x branch, and the compatibility table shows that line reaching only OpenAPI 1.1 and 1.2. If you inherited a Swagger 2.0-era integration, that branch is where the code is, and it is not the branch that receives 3.2.0 support.

## One repository, four packaging pipelines, and a test suite that needs ports

The top level tells you what has to keep working on every change. There is dist/ and swagger-ui-dist-package/ for npm, composer.json for PHP projects, snapcraft.yaml for a Snap package, and a Dockerfile with a docker/ directory for the container image. Alongside them sit flavors/, config/, webpack/, dev-helpers/, release/ and test/, which is the machinery behind those outputs rather than the application itself.

The browser targets are not hardcoded either. A .browserslistrc file is present, and the stylesheet build sets BROWSERSLIST_ENV to browser-production, so which browsers the CSS is compiled for is a config question with a named environment.

For contributors, the end-to-end suite is the practical constraint. It runs on Cypress, and the full suite is npm run cy:ci, which starts the servers it needs, runs Cypress headless and shuts them down afterwards. The README adds a warning not to have a dev server running on the same port, which is the kind of collision that costs an afternoon the first time it happens and is cheap to avoid by reading the line.

## Conclusion

Choose Swagger UI when your API is already described in OpenAPI and you want a documentation page that can call the API, and pick your package by build system: swagger-ui for a bundler, swagger-ui-dist only when dependencies cannot be resolved, swagger-ui-react inside React. Check the compatibility table before you pin, because 3.2.0 support arrives at 5.32.0 and nothing older covers it. If you serve the Docker image anywhere public, change CORS, which ships as true, and know that the non-root setup is a chmod simulation rather than an unprivileged base. And set SCARF_ANALYTICS=false or scarfSettings.enabled before an install reaches a network you do not control.

## FAQ

### What is Swagger UI used for?

Swagger UI turns an OpenAPI description into a documentation page you can interact with, so a development team or an end consumer can see and call the API's resources without any implementation logic behind it. It is a collection of HTML, JavaScript and CSS assets rather than a server.

### Is Swagger outdated?

The compatibility table is the answer to that, and it is version specific. The OpenAPI Specification has had five revisions since 2010, and the 5.32.0 row from 2026-02-27 is the first to include spec version 3.2.0, while 5.19.0 stops at 3.1.2 and 4.0.0 stops at 3.0.3.

### Is Swagger API free?

The code is under the Apache-2.0 licence, and the README names no price for it. The repository ships the swagger-ui, swagger-ui-dist and swagger-ui-react packages on npm, plus a release you can download and serve yourself from the /dist folder.

### How do I install Swagger UI?

Pick the package that matches your build: swagger-ui for a single-page application with a bundler, swagger-ui-dist when dependencies cannot be resolved, swagger-ui-react for React. If you want no package manager at all, download the latest release and copy the /dist folder to your own server.

### Can I use Swagger UI for API testing?

It is built to let you interact with the API's resources from the rendered description, which is what makes trying a request from the page possible. The README does not describe a test suite, assertions, or any kind of reporting on top of that.

### How do I access Swagger UI on localhost?

In the Docker image the port is set to 8080 and the description is read from /app/swagger.json inside the container, so that is the path the image expects. The README does not give a run command for the image or a localhost URL, and the Dockerfile itself points at the configuration documentation for the environment variables.

## Sources

- [License: Apache-2.0](https://github.com/swagger-api/swagger-ui/blob/main/LICENSE)
- [Project website](https://swagger.io)
- [README](https://github.com/swagger-api/swagger-ui/blob/main/README.md)
- [Releases](https://github.com/swagger-api/swagger-ui/releases)
- [swagger-api/swagger-ui on GitHub](https://github.com/swagger-api/swagger-ui)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/swagger-api-swagger-ui
