Drop a PDF into a web page and you usually end up picking one of two uncomfortable options: embed PDF.js's own viewer in an <iframe> (works, but you're stuck behind an iframe boundary — no theming, no styling it to match your app), or pull in pdfjs-dist directly and build your own toolbar, zoom controls, search, and page navigation from PDF.js's fairly low-level rendering API.
lit-pdf-viewer is a native web component that skips both compromises: a real custom element (not an iframe), with a full toolbar already built, that you can theme like any other element on your page.
Why a custom element, specifically
<lit-pdf-viewer> is a Web Component — no React/Vue/Angular adapter needed. Because custom elements are a browser primitive rather than a framework integration, it behaves identically everywhere:
<script type="module">
import 'lit-pdf-viewer';
</script>
<lit-pdf-viewer src="path/to/document.pdf"></lit-pdf-viewer>
Or inside a Lit component, same element, same API:
import 'lit-pdf-viewer';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
@customElement('my-app')
class MyApp extends LitElement {
render() {
return html`<lit-pdf-viewer src="path/to/document.pdf"></lit-pdf-viewer>`;
}
}
It fills the height of its host, so give it one:
lit-pdf-viewer {
display: block;
height: 100vh;
}
The bundle cost, honestly
Adding the component costs about 17 KB gzipped — that's the whole thing, Lit runtime included, before you've loaded a single PDF. PDF.js itself (~196 KB gzipped) is only pulled in via a dynamic import() once a <lit-pdf-viewer> actually connects to the page or a print is triggered — not just for having the package listed in your dependencies.
What's in the toolbar
Not a bare canvas — the toolbar, search, and page controls ship built in:
- Zoom: page-fit, page-width, actual size, or a custom numeric level
- Page navigation: previous/next, first/last, direct page input
- Text search, including multi-phrase search and a whole-word mode
- Rotate clockwise / counter-clockwise
- Text selection and hand-tool modes, full-screen, download, print (with an optional separate higher-quality print source)
- Keyboard accessibility
const terms = ['invoice total', 'due date'];
html`
<lit-pdf-viewer src="invoice.pdf" .searchQueries=${terms}></lit-pdf-viewer>
`;
<!-- Serve a screen-optimized PDF but print a higher-quality one -->
<lit-pdf-viewer src="document-screen.pdf" printSrc="document-print.pdf"></lit-pdf-viewer>
Theming through the shadow boundary
Every visual surface — toolbar background, buttons, icons, the search bar, tooltips, the error banner — is driven by CSS custom properties with sensible defaults. Set them on the element itself, or any ancestor (:root included), and they inherit down through every nested shadow root automatically — no ::part() gymnastics required for the common case:
lit-pdf-viewer {
--litpdf-toolbar-background: #1e1e1e;
--litpdf-icon-color: #eee;
--litpdf-accent-color: #ff6b35;
--litpdf-surface-background: #2a2a2a;
--litpdf-text-color: #eee;
}
i18n out of the box
Toolbar, search bar, error panel, and loading indicator are translated in English, French, Spanish, and German, auto-detected from navigator.language with English as the fallback:
<lit-pdf-viewer src="doc.pdf" locale="es"></lit-pdf-viewer>
Override individual strings (or add a language of your own) without losing the rest of the resolved locale:
html`
<lit-pdf-viewer
src="doc.pdf"
locale="en"
.translations=${{
toolbar: { print: 'Print document' },
search: { placeholder: 'Find in this document...' },
}}
></lit-pdf-viewer>
`;
Where this sits relative to the alternatives
A few other PDF-in-the-browser web components already exist, and they're worth knowing about: pdfjs-viewer-element wraps PDF.js's own default viewer inside an isolated <iframe> — a solid drop-in choice if you don't need it to look like the rest of your app. pdf-viewer-element (from Lit's own creator) stays intentionally close to the metal — a rendering primitive rather than a full toolbar. lit-pdf-viewer targets the space between the two: a real, styleable custom element with the toolbar, search, and i18n already built, for the common case of "I want a PDF viewer in my page that looks like it belongs there."
Install
npm install lit-pdf-viewer
npx lit-pdf-viewer-install <your-public-dir>
The second command copies the PDF.js worker, CMaps, and icon font into your public directory — required static assets the component serves alongside your app.
- npm: lit-pdf-viewer
- source: github.com/korial29/lit-pdf-viewer
- live demo: korial29.github.io/lit-pdf-viewer
Apache-2.0 licensed. Issues and PRs welcome.

Top comments (0)