Quick start
This walkthrough opens a document on your CloudPDF server, lists its pages, and renders the first one to an image — entirely from the client, using a document-scoped token your backend mints.
1. Install and create an engine#
npm install @cloudpdf/engine@nextimport { createCloudEngine } from '@cloudpdf/engine';
const engine = createCloudEngine({
baseUrl: 'https://pdf.your-app.com',
});2. Open a document#
For share-link and embed flows, open with a doc-scoped JWT. The engine reads
the doc_id claim from the token (without verifying it — the server is the
verifier of record), then fetches the document head.
const doc = await engine.open({
kind: 'token',
token: () => fetchDocToken(), // your backend mints this
});
console.log(doc.id);If your frontend already holds a tenant session and knows the document id, open by id instead:
const doc = await engine.open({ kind: 'id', id: 'doc_proposal_2026' });The two cloud open modes are kind: ‘token’ and
kind: ‘id’. The local-engine modes (‘bytes’,
‘layerBytes’) are rejected — the cloud engine never receives raw
PDF bytes.
3. List pages and render the first one#
Pages are addressed by their durable page object number, never by index.
pages.list() returns every page’s geometry in display order, so grab the first
one’s pageObjectNumber and render it.
const { pages } = await doc.pages.list();
const first = doc.page(pages[0].pageObjectNumber);
const image = await first.render.image({
viewport: { kind: 'width', width: 1200 }, // 1200px wide, aspect preserved
format: 'webp',
});
const { url, revoke } = await image.objectUrl();
document.querySelector('img')!.src = url;
// Call revoke() once the <img> no longer needs the object URL.render.image() returns a PageImageHandle describing the encoded result
(format, contentType, width/height) plus an objectUrl() helper for the
browser. For raw pixels (e.g. canvas compositing) use first.render.raw(),
which returns an RGBA PageRaster — note this is local-engine only; the
cloud engine serves encoded images, so use render.image() there.
4. Read the page text (optional)#
const { text, charCount } = await first.text.read();
console.log(`page has ${charCount} characters`);5. Clean up#
Close the handle when you’re done with a document, and destroy the engine when your app shuts down.
await doc.close();
await engine.destroy();Cancelling work#
Every async call returns an AbortablePromise. Calling .abort() rejects it
with an AbortError and signals the in-flight request to stop.
const pending = first.render.image({ viewport: { kind: 'scale', scale: 2 } });
// Changed your mind (user scrolled away)?
pending.abort();