ui-058

PDF Document Viewer

£5

A PDF reader with a page control that expands into visual navigation, searchable documents and a reviewed HTML reading mode for phones.

Flip advanced
4 more details
flip-layoutstate-transitionkeyboard-navigationfocus-management
PDF Document Viewer - UI component preview

This element reads better at your own screen size than in the frame below:

About this component

Let readers browse the original PDF, find a page quickly and switch to a reviewed reading version without losing their place. The compact page-position control expands into thumbnails and contents with a connected Flip transition. On phones it becomes a bottom sheet, with page controls kept within reach.

Read the full effect overview

The Original view renders real PDFs with selectable text, links, search and zoom. Reading mode uses an author-supplied HTML companion with page mappings, comfortable text and adjustable size. An original eight-page home energy guide demonstrates both views. Local files stay in the browser, and opening another document clears the previous companion.

What's included

11 items
  • Original PDF rendering with selectable text, links, fit-width, fit-page and zoom steps
  • Page-position control expands into a thumbnail navigator and document contents
  • Mobile navigator with focus containment, inert background and focus return
  • Incremental search with match highlighting, next/previous and no-text states
  • Reviewed HTML reading edition with page mappings and 16–24px text controls
  • Left, centre and right alignment with fullscreen quiet reading and zoom restoration
  • Download the original PDF or an explicitly matched clean Reading PDF
  • Local PDF input, password recovery and a direct original-document route
  • Bounded page rendering and thumbnail queues with cancellation and teardown
  • Independent instances, documented methods and root-scoped events
  • Light and dark UI themes; PDF page colours remain unchanged

Perfect for

4 use cases
  • Reports that need both a faithful PDF layout and comfortable phone reading
  • Product guides and handbooks with visual page navigation
  • Brochures with a reviewed HTML companion
  • Document libraries that render files locally without a hosted conversion service

How it works

1 section

PDF.js renders the source document while an author-controlled companion provides the reading edition. Page mappings connect the two views. The position control and navigator share a frame, so GSAP Flip keeps the current-page indicator visually attached as the panel changes size. Rendering and search cancel obsolete work when documents change; each instance owns its own state and cleanup.

Plugins Flip
Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this UI element's demo, 15 September 2026. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid UI element

Purchase to unlock the code.

Buying PDF Document Viewer opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£5 Standard license, unlimited projects

Browse UI elements

Documentation

Quick Start

Serve the folder over HTTP or HTTPS. ES modules and PDF workers do not work reliably from a file:// URL.

In the head:

Code snippet omitted: it ships with the download.

In the body:

Code snippet omitted: it ships with the download.

Apply data-variant="light" or data-variant="dark" to the body or component root. Keep the original link and companion in the initial markup: they remain available without JavaScript. A <template> is also accepted, but a template alone is not a no-JavaScript fallback.

Using It With Your Own Design

The mount root owns its height. Give it a definite height or a height resolved by your host's layout. Everything the viewer creates is scoped under .pdfv; the script does not query demo stage classes. Copy the component assets and retain their relative paths.

Re-value --ground, --ground-2, --raised, --hover, --ink, --ink-2, --ink-3, --line, --input-line, --accent, --accent-text, --selected, --selected-ink, --control-radius, --panel-radius and the two shadow tokens to match your interface. Load Inter yourself, or set --font to your own UI font.

The theme toolbar, .stage, demonstration mounting code and __thumbnail hook in index.html are demo-only furniture. The reader and its document controls are the component. Keep pdf-engine.css: its page, text and annotation positioning is functional. Do not put a transform or CSS scale on a reader while someone is selecting text.

Downloading the Reading Edition

Supply readingPdf with the URL of the PDF that matches your authored readingContent. In Reading view, the toolbar download saves this clean edition; in Original view it saves the source PDF. Document options lists both, including on phones. The reading download appears only when both a companion and its PDF URL are supplied. Invalid or unsupported URL schemes are ignored. Use a same-origin URL for a reliable browser download; cross-origin servers control whether their response downloads or opens.

The supplied home-energy-guide-reading.pdf contains the full clean Reading edition with selectable text, headings, contents links, the diagram and table. It has no viewer controls or search highlights and is independent of the current theme, zoom and quiet mode. The accompanying standalone HTML is the print source, with its styles and illustration embedded.

To regenerate the sample from assets/reading-companion.html, run node source/generate-reading-download.mjs with @playwright/test and Playwright Chromium installed. The PDF is generated ahead of time from the reviewed companion; the viewer does not automatically convert arbitrary uploaded PDFs or generate a new PDF in the reader’s browser.

Opening another document clears the previous reading PDF along with its companion. Pass an explicitly matched readingPdf and readingContent in the replacement options to enable the new document’s download.

Alignment and Quiet Mode

Choose Left, Centre or Right in Document options, or call reader.setAlignment("left"). This moves the PDF pages or the reading article inside the reader, while keeping text left aligned. Fit page and Reading views usually leave room for the alignment to show; Fit width fills the available space. The viewer itself fits its host container, so place that container with your own layout CSS.

Quiet mode expands the reader to fill the browser viewport, with the surrounding page and reader controls out of sight. The PDF grows to a readable width of up to 900px, filling the available width on phones; Reading mode uses at least 20px text in a column up to 760px wide. A 280ms Flip transition connects the embedded and expanded layouts. Use Exit quiet mode or Escape to return. Your previous zoom preference is restored, and your current reading position follows you back, including pages visited while quiet. There is no idle timer.

A native modal dialog places the reader above host clipping and transforms while retaining inherited theme tokens. Keyboard focus enters the document so PageDown works immediately. The dialog contains focus and makes the surrounding page inert; host scroll and Lenis state are restored on exit or teardown. Only one reader owns quiet mode at a time. Opening any reader’s page navigator exits it. The document, links and error/password recovery remain available, while automatic search jumps pause. Within an iframe, quiet mode fills that frame’s viewport.

Use the toolbar's Quiet mode button (Document options on phones), reader.setQuiet(true), or the quiet: true mount option. Alignment and quiet mode remain selected when opening another PDF. The demo strip exposes both choices for previewing them. Its desktop preview starts in Fit page so alignment is immediately visible; the component defaults to Fit width.

What's Included

  • assets/script.js: editable component source.
  • assets/pdf-engine.js and pdf-engine.css: PDF.js rendering, text selection, links and bounded rendering queues.
  • assets/vendor/pdfjs/: pinned PDF.js display module, matching worker, character maps, standard fonts, WASM and licence notices.
  • assets/vendor/pdfjs/dependency-lock.json: dependency pin and package integrity; assets/vendor/pdfjs/README.md: hosting and licence notes.
  • assets/home-energy-guide.pdf: eight original pages with an outline, links, a two-column layout, diagram and table.
  • assets/home-energy-guide-reading.pdf and home-energy-guide-reading.html: clean reading downloads and self-contained print source.
  • source/generate-reading-download.mjs: reproducible clean HTML/PDF generation.
  • assets/reading-companion.html: reviewed semantic HTML; its content is embedded in the demo for the no-JavaScript fallback.
  • assets/energy-diagram.svg, assets/provenance.md and source/generate-guide.py: original illustration, provenance and reproducible guide source.
  • thumbnail.html: deterministic catalogue capture input, excluded from buyer downloads.

Reading Companion Contract

Pass an author-controlled Element, DocumentFragment or HTMLTemplateElement as readingContent. The viewer clones it; it never fetches or converts arbitrary HTML. Treat this as trusted application content, with the same review as your page templates. Never pass untrusted markup from an upload or remote document.

Each mapped section needs a stable id and a one-based data-pdf-page. Multiple sections may map to the same page. Give paragraphs stable IDs too. Own the heading hierarchy, reading order, alt text, figure captions, table captions and header associations. The complete eight-page example shows the contract.

The viewer prefixes cloned IDs and their internal references per instance. Inactive modes are hidden from the accessibility tree. Switching views selects the nearest mapped section/page and announces that the correspondence is approximate. Within a reading session, text resizing preserves a visible text anchor; returning to the same mapped page restores the previous reading anchor. It does not claim exact PDF paragraph coordinates.

Opening another PDF clears the previous companion. To provide an explicitly matched replacement:

Code snippet omitted: it ships with the download.

Without a companion, only Original is offered and Document options explains that no reading version is available. Scanned PDFs report “No searchable text”; OCR and automatic conversion are outside this component. A PDF viewer cannot repair an arbitrary source document's reading order or make it WCAG compliant.

Methods and Events

Method Behaviour
PDFViewer.mount(root, options) Returns an independent reader; mounting an already mounted root returns its existing instance.
await reader.open(source, replacements?) Opens a URL or File; latest request wins. Clears prior companion/search. Resolves current state after success or a displayed error; superseded work resolves without a result.
reader.goToPage(number) Jumps to a valid integer page and announces it; returns false if unavailable or out of range.
reader.setMode('original' | 'reading') Returns false when Reading is unavailable; otherwise maps position.
reader.setZoom('fit-width' | 'fit-page' | number) In Original, numeric scale is 0.5–3. In Reading, number means text pixels, clamped to 16–24.
reader.setAlignment('left' | 'center' | 'right') Aligns document pages or the reading article within the reader; returns false for invalid values.
reader.setQuiet(boolean) Expands to focused reading or restores the embedded layout and zoom, preserving your current place. Returns false for invalid values.
reader.openNavigator() / closeNavigator() Opens or closes page navigation.
reader.getState() Title, page, numPages, mode, zoom, alignment, quiet, ready, loading, readingAvailable, readingPdfAvailable, navigatorOpen and error. After destroy, returns {destroyed:true, ready:false}.
reader.destroy() / reader.revert() Cancels work, releases resources and restores the root's original nodes and class. Remount to reuse.

Mount options are source, title, readingContent, optional readingPdf, initialPage (1), initialMode (original), alignment (center) and quiet (false). open() replacements accept title, readingContent and readingPdf. Initial settings apply only to the initial document.

Events fire on the mount root, with payload in event.detail:

Event Payload
pdfviewer:ready {numPages, title, readingAvailable}
pdfviewer:pagechange {page, numPages, mode, explicit}; native scrolling updates state without live announcements.
pdfviewer:modechange {mode, page, approximate:true}
pdfviewer:alignmentchange {alignment}
pdfviewer:quietchange {quiet}
pdfviewer:error {name, message}

Keyboard & Accessibility

Tab follows the toolbar, any open search controls, active document and bottom controls. Enter/Space activate buttons. No global arrow-key or find shortcut is captured, and browser pinch zoom remains available.

Context / key Behaviour
Quiet mode / Escape Returns to the embedded reader and focuses Quiet mode (Document options on phones).
Navigator / Escape Closes and returns focus to the page-position button.
Navigator tabs / arrows, Home, End Selects adjacent, first or last tab.
Page field / Enter Validates the document range and jumps.
Native page range / arrows Previews and commits a page using native keyboard behaviour.
Search / Enter or Shift+Enter Next or previous result.
Search or Document options / Escape Closes the surface and restores its trigger's focus.
Mobile navigator / Tab or Shift+Tab Keeps focus inside the labelled modal dialog.

Desktop navigation is an anchored dialog without a focus trap. Outside interaction closes it. On phones it is a modal bottom sheet: the rest of the page is inert, scrolling is locked, and prior state returns on close. Opening another reader's mobile modal releases the previous reader's lock first. A polite status region announces ready, explicit jumps, errors and mode changes, without rereading the whole document on every scroll.

Reading headings, tables and images stay semantic. Internal reading links move focus to their destination. PDF text/link accessibility still depends on source content; keyboard navigation and a reviewed companion do not replace a source document accessibility review. Perform a manual screen-reader pass with your target browsers and document before release.

With reduced motion, navigation and quiet mode change state immediately. With GSAP blocked, all navigation remains functional. With PDF.js or its worker blocked, the source link and available HTML edition still work. Without JavaScript, initial author-supplied content remains readable.

How It Works

The page-position control and expanded navigator share a frame. GSAP Flip records their layout and animates the width, height and position over 220ms. Page content is never curled or animated in scale.

PDF.js supplies page pixels, text layers, links, outlines and search text. Only visible pages and a small buffer are rendered, with at most five full-size canvases, two concurrent page renders, a four-megapixel cap and a 4096px dimension cap. The thumbnail queue is separate: two renders at a time and an 80-image cache. Search yields between pages and within dense results, and obsolete jobs are cancelled on replacement. A cancelled search cannot overwrite newer UI state.

Themes

Light is the default, with neutral grey surroundings, raised controls and electric-blue selection. Dark uses the same tokens and layout. PDF pages retain their original colours; the reader never inverts page images. The HTML edition follows the selected theme. ?variant=dark is a demo convenience.

Requirements and Hosting

  • Modern browsers supporting ES modules, module workers, native modal dialogs, ResizeObserver, IntersectionObserver, inert and PDF.js 5.4.624. Verify your supported browser versions against PDF.js's browser requirements.
  • GSAP 3.12+ with Flip for the transition. The demo uses the centrally pinned GSAP 3.15.0. Both are optional for operation.
  • Self-host all assets/vendor/pdfjs/ files. Display module and worker must have identical versions. See assets/vendor/pdfjs/README.md before updating.
  • Serve .js / .mjs as JavaScript, .wasm as application/wasm, and .pdf as application/pdf.
  • Remote PDFs need the source server's CORS permission. No server proxy is included. Same-origin files are simplest. PDF requests do not send cross-origin credentials.
  • Allow your self-hosted scripts, worker and fonts in your CSP. The demo's optional GSAP and Google Fonts URLs need their respective allowlists; self-host those too if your policy requires it. PDF.js may use blob URLs for internal resources.
  • Embedded PDF scripts and XFA are not enabled. The viewer exposes PDF links but does not implement form editing, annotation authoring or attachments.
  • Local File data stays in the browser. Owned object URLs, workers, render jobs, listeners, observers and GSAP contexts are released by teardown.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

The Vault library, plus future additions to the library.