Lector

Your first viewer

A complete viewer with selectable text, page and zoom controls, and explicit layout.

Complete the installation first. This example imports the pdf-setup.ts you created there and loads public/sample.pdf. In Next.js, render it through the browser-only wrapper from that guide.

A viewer you can build on

pdf-viewer.tsx
"use client";
 
import {
  CanvasLayer,
  CurrentPage,
  CurrentZoom,
  Page,
  Pages,
  Root,
  TextLayer,
  TotalPages,
  ZoomIn,
  ZoomOut,
} from "@anaralabs/lector";
import "./pdf-setup";
 
export default function PDFViewer() {
  return (
    <Root
      source="/sample.pdf"
      isZoomFitWidth
      style={{ height: 600, display: "flex", flexDirection: "column" }}
      loader={<p role="status">Loading PDF…</p>}
    >
      <div
        role="group"
        aria-label="PDF controls"
        style={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: 8, padding: 8 }}
      >
        <label>
          Page <CurrentPage style={{ width: 56 }} />
        </label>
        <span>of</span>
        <TotalPages />
        <ZoomOut type="button" aria-label="Zoom out">−</ZoomOut>
        <label>
          Zoom <CurrentZoom style={{ width: 56 }} /> %
        </label>
        <ZoomIn type="button" aria-label="Zoom in">+</ZoomIn>
      </div>
      <div style={{ flex: 1, minHeight: 0 }}>
        <Pages>
          <Page>
            <CanvasLayer />
            <TextLayer />
          </Page>
        </Pages>
      </div>
    </Root>
  );
}

CurrentPage and CurrentZoom are editable inputs, so they need labels. The zoom buttons have no default text; supply their children. CurrentPage commits when the input loses focus or you press Enter.

How the pieces fit

Root                     Loads one document and provides its store
├── Your toolbar         Reads and changes that document's state
└── Pages                Owns the scroll container and virtualizes pages
    └── Page             A template cloned for each visible page
        ├── CanvasLayer  Paints the page
        └── TextLayer    Adds selectable text over the canvas

Pass one Page template to Pages. It supplies the page number; you do not map over the document yourself. Page numbers in Lector's public navigation APIs start at 1.

Root only mounts its children after the document and page viewports are ready. Components using usePdf, usePdfJump, or other viewer hooks must be descendants of Root. Page-specific layers and usePDFPageNumber also need a Page ancestor. A hook in the same component that returns Root is still outside that provider.

Give the pages room to scroll

Pages defaults to height: 100% and owns its scrolling. Give its parent a definite height. With a toolbar, use a flex column and put Pages in a flex: 1; min-height: 0 wrapper, as above. In a sidebar layout, also set min-width: 0 on the viewer column.

Avoid overriding Page dimensions or applying your own scale transform: Lector uses its viewports to align canvas, text, and highlights.

Choose your layers

LayerAddsGuide
CanvasLayerVisible PDF contentBasic example
TextLayerSelection and copying when the PDF contains textText selection
AnnotationLayerExisting PDF links and form widgetsLinks, forms
HighlightLayerRectangles from the viewer's highlights stateHighlights
ColoredHighlightLayerSelection color tools and colored highlightsText selection

Place canvas first, then text, then the interaction or highlight layers you need. Loading failures need application UI outside Root; see loading and errors.

Next, add page navigation, search, or dark mode.

On this page