One page, one config, one script.
The whole install
<!doctype html>
<html>
<head><title>My API docs</title></head>
<body>
<script id="api-doc-config" type="application/json">
{ "openapi": { "url": "https://example.com/openapi.json" } }
</script>
<script src="https://cdn.jsdelivr.net/npm/apiglow@0.1.0/dist/app.js" type="module"></script>
</body>
</html>That is not a teaser — it is the complete installation. app.js self-initializes: it reads the config block, injects its own CSS (resolved next to the bundle), and builds the entire UI in the page. Nothing to build, nothing to generate, nothing to run on a server.
That src points at a public CDN. If you would rather depend on nothing outside your own origin, serve the bundle yourself from npm — the config block below does not change, only that one line.
Two details are deliberate and worth knowing:
- The config element’s id is
api-doc-config, and the fallback global iswindow.API_DOC_CONFIG. Theapidocprefix is intentional and stable, so that a product rename never invalidates a deployed page or its readers’ stored data — don’t “correct” it toapiglow; copy it verbatim. - The config is strict JSON (it lives in a
type="application/json"script): no comments, no trailing commas. To keep a commented config, use thewindow.API_DOC_CONFIGfallback in a regular script instead.
openapi.url or openapi.spec
Only one key is required: where the schema is.
openapi.urlpoints at an OpenAPI 3.0.x / 3.1.x / 3.2.x document, JSON or YAML (a Swagger 2.0 one is converted at load). If the server hosting it is not the docs’ origin, it must allow cross-origin reads — see Try-it & CORS.openapi.speccarries the document itself, as a JS object or a JSON string: no request, so no CORS to negotiate. In exchange the page carries the schema’s weight and must be redeployed on every API change. When both are set,specwins.
Several APIs in one installation is openapi.specs — that one has its own guide.
A fuller config
Everything beyond openapi has a sensible default; the block below shows the shape of the common options. The configuration reference documents every key.
{
"openapi": { "url": "https://example.com/openapi.json" },
"theme": { "default": "light", "available": ["light", "dark", "corporate"] },
"language": { "default": "en", "available": ["en", "fr"] },
"environments": [],
"docsPages": [],
"scenarios": [],
"features": { "scenarios": true, "audit": true },
"tryIt": { "proxyUrl": null },
"history": { "maxEntries": 500, "maxAgeDays": 30 }
}Hosting
Any static HTTP(S) hosting is enough — an S3 bucket, GitHub Pages, a folder behind nginx. file:// does not work: the app is an ES module and fetches its assets, and browsers block both from local files. For a quick local look, any one-line static server does the job (npx serve, python3 -m http.server).
Serving it yourself, from npm
The snippet above loads the bundle from a public CDN. If you would rather your documentation depend on nothing outside your own origin — an intranet, an air-gapped network, a CSP with no third party in it — install the package and serve its dist/ yourself. Exactly one line of the snippet changes.
npm install apiglow
cp -r node_modules/apiglow/dist static/apiglow<script src="/apiglow/app.js" type="module"></script>The config block is untouched. What you gain is the version pinned in your lockfile — reviewed, audited and upgraded like any other dependency — and not one request leaving your origin at runtime.
Whatever your build already does to publish static files will do: Vite’s publicDir, a webpack copy plugin, a cp -r in CI. There is nothing to compile.
Keep app.js next to the rest of dist/. It finds app.css, the i18n/ language packs and fonts/ relative to itself, so the four have to stay siblings wherever you publish them — copy the directory, not the file.
That same rule is the reason ApiGlow does not go through your JavaScript bundler. Inlined into one of your chunks, app.js starts resolving its stylesheet and its language packs next to your code, finds neither, and renders the documentation unstyled. Install it, serve it, point a <script> at it — that is the supported shape, and it is the one that keeps the app a single file you can read.
Let an agent do it
If an agent is doing the installing, hand it this prompt as-is:
Add interactive API documentation to my project with ApiGlow
(https://apiglow.dev).
1. Create a static page (e.g. docs/index.html) containing exactly:
- a <script id="api-doc-config" type="application/json"> block with:
{ "openapi": { "url": "<my OpenAPI schema URL or relative path>" } }
- <script src="https://cdn.jsdelivr.net/npm/apiglow@0.1.0/dist/app.js"
type="module"></script>
2. My OpenAPI schema is at: [FILL IN — URL or path]
3. Serve the page over HTTP(S) — file:// does not work (ES modules).
4. If my API is on another origin, tell me it must allow CORS from the
docs origin for the try-it console to work.
Config reference: https://apiglow.dev/docs/configuration/