Skip to content

Getting Started

Run OpenNav after your existing static build command. The output folder must already contain real prerendered HTML such as index.html, docs/getting-started/index.html, or docs/api/index.html.

Install OpenNav in the project that builds your static site.

Terminal window
npm install @opennav-ai/opennav
Terminal window
opennav build --static --output dist --site-url https://example.com --site-name "Example Docs"

Use --dry-run first when you want to preview the exact files OpenNav would create, modify, or skip.

Terminal window
opennav build --static \
--output dist \
--site-url https://example.com \
--site-name "Example Docs" \
--dry-run

OpenNav is designed for static site platforms first. If your deployment is a folder of files, OpenNav can run before you upload that folder.

Platform styleExample outputHow OpenNav fits
Cloudflare Pagesdist/ or framework outputRun OpenNav after the build with --platform cloudflare-pages, then deploy the same folder.
Netlifydist/, build/, or framework outputAdd OpenNav as the final build step before publish.
Vercel static outputout/ or generated assetsRun OpenNav for exported static routes before upload.
GitHub Pages or CDN hostingPlain HTML folderRun OpenNav locally or in CI, then publish the generated files.

Use the TypeScript SDK when OpenNav should run inside a framework build or a custom Node script. Static Astro and Next.js export builds are the first framework-specific SDK paths.

FrameworkCurrent supportOutput folder
AstroStatic build helper.dist/
Next.jsStatic export helper for output: "export".out/

More framework helpers will follow in coming releases.

OpenNav supports runtime HTML-to-Markdown content negotiation for any Node.js or WinterCG-compatible server. Import OpenNavServer from @opennav-ai/opennav/server and wrap your existing route handlers:

import { OpenNavServer } from "@opennav-ai/opennav/server";
const opennav = new OpenNavServer();
app.get("/docs/:slug", async (c) => {
const htmlResponse = await renderPage(c.req.param("slug"));
const result = await opennav.negotiate({
request: c.req.raw,
htmlResponse,
});
if (result.isErr()) return c.text("Internal error", 500);
return result.value;
});

When the client sends Accept: text/markdown, the HTML is converted to Markdown on-the-fly. Browser requests still receive normal HTML, and responses include Vary: Accept so caches keep both representations separate.

See the Server-Side Frameworks guide for framework-specific advice.