DocsSvelteFull Example: Svelte & Tailwind PDF Viewer

Full Example: Svelte & Tailwind PDF Viewer

While the previous guides show how to use individual plugins, it’s often helpful to see how they all come together in a complete application. This page showcases a feature-rich, responsive PDF viewer built with EmbedPDF, Svelte, and the Tailwind CSS utility framework.

This example serves as a practical blueprint, demonstrating how to build a polished and cohesive user experience by combining EmbedPDF’s headless components with a popular styling solution.

ℹ️

This example is a great starting point for your own project. Feel free to fork the repository and adapt the code to fit your needs.

Features Showcase

The Svelte example integrates numerous plugins to create a comprehensive viewing experience.

FeaturePlugin(s) Used
Virtualized scrolling with smooth page rendering@embedpdf/plugin-scroll + @embedpdf/plugin-render + @embedpdf/plugin-tiling
Advanced zoom controls (presets, fit-to-page, marquee)@embedpdf/plugin-zoom
Pan/hand tool for easy navigation@embedpdf/plugin-pan
Page rotation in 90-degree increments@embedpdf/plugin-rotate
Single and two-page spread layouts@embedpdf/plugin-spread
Thumbnail sidebar for quick page jumping@embedpdf/plugin-thumbnail
In-document text search with result highlighting@embedpdf/plugin-search
File opening, downloading, and printing@embedpdf/plugin-document-manager, @embedpdf/plugin-export, @embedpdf/plugin-print
Fullscreen mode@embedpdf/plugin-fullscreen
Text selection and copy-to-clipboard@embedpdf/plugin-selection

Core Concepts Illustrated

1. Centralized Plugin Registration

All features are enabled and configured in a single plugins array within the main Application.svelte component. This makes it easy to see what capabilities the viewer has and to add or remove features.

src/lib/components/Application.svelte
<script lang="ts"> // ... const plugins = [ createPluginRegistration(DocumentManagerPluginPackage, { /* ... */ }), createPluginRegistration(ViewportPluginPackage, { /* ... */ }), createPluginRegistration(ScrollPluginPackage, { /* ... */ }), createPluginRegistration(RenderPluginPackage), createPluginRegistration(TilingPluginPackage, { /* ... */ }), createPluginRegistration(ZoomPluginPackage, { /* ... */ }), createPluginRegistration(SearchPluginPackage), createPluginRegistration(InteractionManagerPluginPackage), createPluginRegistration(PanPluginPackage), createPluginRegistration(RotatePluginPackage), createPluginRegistration(SpreadPluginPackage), createPluginRegistration(FullscreenPluginPackage), createPluginRegistration(ExportPluginPackage), createPluginRegistration(ThumbnailPluginPackage), createPluginRegistration(SelectionPluginPackage), // ... and more ]; </script> <EmbedPDF engine={pdfEngine.engine} {plugins}> {#snippet children({ activeDocumentId })} {#if activeDocumentId} {@const documentId = activeDocumentId} <DocumentContent {documentId}> {#snippet children(documentContent)} {#if documentContent.isLoaded} <Toolbar {documentId} /> <Viewport {documentId}> <!-- Viewer UI with documentId passed to components --> </Viewport> {/if} {/snippet} </DocumentContent> {/if} {/snippet} </EmbedPDF>

2. Reusable Svelte Components

The UI is broken down into logical, reusable Svelte components, each leveraging Tailwind CSS for styling (e.g., <Toolbar.svelte>, <Sidebar.svelte>, <Search.svelte>). Each component is responsible for a specific piece of the UI and uses EmbedPDF stores to interact with the relevant plugins.

For example, the <Toolbar.svelte> component uses multiple stores to manage different functionalities like panning, rotating, and changing the page layout. Since EmbedPDF supports multiple documents, stores require a documentId parameter (as a getter function).

src/lib/components/Toolbar.svelte
<script lang="ts"> import { usePan } from '@embedpdf/plugin-pan/svelte'; import { useRotate } from '@embedpdf/plugin-rotate/svelte'; import { useSpread } from '@embedpdf/plugin-spread/svelte'; // ... other imports interface Props { documentId: string; } let { documentId }: Props = $props(); // Pass documentId as a getter function to each store const pan = usePan(() => documentId); const rotate = useRotate(() => documentId); const spread = useSpread(() => documentId); // ... </script> <div class="toolbar"> <!-- ... --> <button class:active={pan.state.isPanning} onclick={() => pan.provides?.togglePan()} > Pan </button> <!-- ... other buttons for rotate, spread, etc. --> </div>

3. Responsive Design

The example uses standard CSS media queries and Tailwind’s responsive prefixes (md:, lg:) to adapt the layout for smaller screens. On mobile, sidebars automatically collapse, and some toolbar icons are hidden to save space, providing a more native-like experience.

Running the Example Locally

You can run the full Svelte example on your machine to experiment with the code.

Prerequisites: You’ll need Node.js 18+ and pnpm.

1. Clone the repository:

git clone https://github.com/embedpdf/embed-pdf-viewer.git cd embed-pdf-viewer

2. Install dependencies:

pnpm install

3. Build the core packages

The example imports the core EmbedPDF packages from the local workspace. You need to build them once.

pnpm run build --filter "./packages/*"

4. Run the example’s dev server:

pnpm --filter @embedpdf/example-svelte-tailwind run dev

Vite will start the development server, and you can access the viewer at http://localhost:3000.

Last updated on December 5, 2025

Need Help?

Join our community for support, discussions, and to contribute to EmbedPDF's development.