This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/web/viewer/production-runtime-setup-checklist.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Production runtime setup checklist | Nutrient Web SDK

Use this page as the canonical runtime path for shipping a stable Web SDK deployment.

Assets mode and serving

Choose one runtime asset mode:

  • CDN mode (useCDN: true) for fastest setup.
  • Self-hosted mode (baseUrl) for controlled/compliant/offline deployments.

For self-hosting, serve the entire assets bundle with its original folder structure, including the nutrient-viewer-lib/ directory. Set baseUrl to the URL above that folder.

await NutrientViewer.load({
container: "#viewer",
document: "/sample.pdf",
baseUrl: "https://example.com/assets/"
});

This makes the SDK load files such as:

https://example.com/assets/nutrient-viewer-lib/...

Bundler with entry point wiring

Confirm your app entry point imports and initializes the viewer only after the mount container meets all of the following conditions:

  • Exists
  • Resolves to a valid element or CSS selector
  • Is empty
  • Has non-zero width and height
import NutrientViewer from "@nutrient-sdk/viewer";
await NutrientViewer.load({
container: "#viewer",
document: "/sample.pdf",
useCDN: true
});

License and environment configuration

Make sure to set up your license key and any other environment-specific configuration securely:

  • Inject licenseKey from environment/configuration in production.
  • Don’t hardcode secrets in client code.
  • For server-backed (Document Engine-backed mode) flows, use short-lived JSON Web Tokens (JWTs) and refresh them with onAuthFailed. Then call instance.setSession(newJwt).

Below is an example of handling JWT expiration and renewal in Web SDK with Document Engine mode:

let instance;
instance = await NutrientViewer.load({
container: "#viewer",
documentId: "doc-id",
serverUrl: "https://your-document-engine.example.com/",
authPayload: { jwt: initialJwt },
onAuthFailed: async () => {
const newJwt = await fetchNewJwtFromBackend();
instance.setSession(newJwt);
}
});

Runtime verification steps

Before deploying, verify the following runtime behaviors in a staging environment that closely matches production:

  • Viewer mounts successfully in intended container.
  • Runtime assets load from expected host/path.
  • Document open, page navigation, and toolbar actions work.
  • Error handling works — test invalid documents, network failures.
  • Document switching unload/reload works without leaks.
  • Export works through the instance API (await instance.exportPDF()), including any selected export options.

Minimal runnable starter templates

Below are minimal runnable templates for both vanilla and React setups using Vite. You can clone these and modify as needed for your production app.

Vanilla (Vite)

Set up a vanilla JavaScript app with Vite and the Web SDK:

viewer-runtime-vanilla/
├─ package.json
├─ index.html
├─ src/main.js
└─ public/
├─ sample.pdf
└─ another.pdf

Install dependencies and define scripts:

package.json
{
"name": "viewer-runtime-vanilla",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@nutrient-sdk/viewer": "latest"
},
"devDependencies": {
"vite": "latest"
}
}

Create the HTML mount container and document-switch controls:

index.html
<div>
<button id="docA">Open A</button>
<button id="docB">Open B</button>
</div>
<div id="viewer" style="height: 100vh"></div>
<script type="module" src="/src/main.js"></script>

Initialize the viewer and wire lifecycle-safe document switching in src/main.js:

src/main.js
import NutrientViewer from "@nutrient-sdk/viewer";
const container = document.querySelector("#viewer");
let instance;
async function openDocument(path) {
try {
// Always unload previous instance to prevent memory leaks.
if (instance) NutrientViewer.unload(container);
// Load new document — CDN mode for fastest setup.
instance = await NutrientViewer.load({
container,
document: path,
useCDN: true
});
} catch (error) {
// Log with context for easier debugging.
console.error(`Failed to load document ${path}:`, error.message);
// Could show user-friendly error in production.
throw error;
}
}
// Initial document load with error handling.
try {
await openDocument("/sample.pdf");
} catch (error) {
console.error("Failed to load initial document:", error.message);
}
document.querySelector("#docA")?.addEventListener("click", async () => {
try {
await openDocument("/sample.pdf");
} catch (error) {
// Handle error appropriately in your app.
console.error("Document A failed to load");
}
});
document.querySelector("#docB")?.addEventListener("click", async () => {
try {
await openDocument("/another.pdf");
} catch (error) {
// Handle error appropriately in your app.
console.error("Document B failed to load");
}
});

React (Vite)

Set up a React app with Vite and the Web SDK:

viewer-runtime-react/
├─ package.json
├─ index.html
├─ src/
│ ├─ main.jsx
│ └─ App.jsx
└─ public/
└─ sample.pdf

Install React and viewer dependencies:

package.json
{
"name": "viewer-runtime-react",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@nutrient-sdk/viewer": "latest",
"react": "latest",
"react-dom": "latest"
},
"devDependencies": {
"@vitejs/plugin-react": "latest",
"vite": "latest"
}
}

Create the Vite HTML entry file:

index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nutrient Viewer React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

Mount the React app in src/main.jsx:

src/main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

Load and unload the viewer in src/App.jsx:

src/App.jsx
import { useEffect, useRef } from "react";
import NutrientViewer from "@nutrient-sdk/viewer";
export default function App() {
const containerRef = useRef(null);
useEffect(() => {
const container = containerRef.current;
if (!container) return;
let cancelled = false;
async function loadViewer() {
try {
// In React.StrictMode, effects run twice in development.
// Unload first to avoid mounting into a container with an existing instance.
NutrientViewer.unload(container);
await NutrientViewer.load({
container,
document: "/sample.pdf",
useCDN: true
});
} catch (error) {
if (cancelled) return;
console.error("Failed to load viewer:", error);
}
}
loadViewer();
return () => {
cancelled = true;
NutrientViewer.unload(container);
};
}, []);
return <div ref={containerRef} style={{ height: "100vh" }} />;
}

Use the following guidance to choose the asset delivery mode that best fits your deployment and compliance requirements.

  • CDN mode — Set useCDN: true.
  • Self-hosted mode — Extract assets into public root and set baseUrl to that served path.

Runtime troubleshooting matrix

Use this matrix to map runtime symptoms to likely causes and the fastest corrective action.

SymptomLikely causeFix
404 on .wasm/worker/font assetsIncorrect baseUrl or incomplete asset extractionVerify baseUrl, extract full archive, preserve directory structure.
load() fails with container errorMount element missing at init timeEnsure container exists before calling NutrientViewer.load(...).
Document fails to loadWrong URL/CORS/unsupported sourceCheck network/CORS headers and document path/format.
License error at startupInvalid/expired/domain-mismatched keyValidate license source, environment injection, and domain constraints.
Switching documents causes leaks or unstable UIPrevious instance not unloadedCall NutrientViewer.unload(container) before reloading.
Toolbar actions disappear after switchToolbar configuration not reapplied on reloadReinitialize toolbar configuration on each load().

Deployment scenarios

Below are some common production deployment considerations.

Single instance vs. multiple viewers

  • Prefer one active instance per mount container.
  • If rendering multiple viewers concurrently, budget memory carefully and unload inactive viewers.

Preview/staging deployment behavior

  • Validate asset paths in the deployed URL base (especially subpath deployments).
  • Smoke-test load, switch, and export in staging before production promotion.

Token/license rotation and configuration hygiene

  • Keep license/runtime configuration environment-driven.
  • Rotate server-backed session tokens using supported runtime hooks.
  • Avoid committing environment-specific runtime values into source.