CloudPDF
DocsPricing
Start building

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@next
import { 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();

Next steps#