# pdfslick: viewing PDFs in React, Solid and Svelte

> PDFSlick wraps Mozilla's PDF.js in a set of framework bindings, with a Zustand store holding the document and viewer state so your own toolbars and sidebars can read it. React, Solid and Svelte each get a package, and the whole thing is a Turborepo monorepo with changesets and a docs site.

**pdfslick/pdfslick** — View and Interact with PDFs in React, SolidJS, Svelte and JavaScript apps

- Repository: https://github.com/pdfslick/pdfslick
- Website: https://pdfslick.dev
- Stars: 1,090 · Forks: 54
- Language: TypeScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/pdfslick-pdfslick

## PDF.js with a store and framework bindings

PDFSlick is a library that enables viewing of and interaction with PDF documents in React, SolidJS, Svelte and JavaScript apps. It is built on top of Mozilla's PDF.js, and it uses Zustand to provide a reactive store for the loaded documents.

That is the whole architecture, and it is a good one for the problem. PDF.js is a mature rendering engine that already draws PDF pages to a canvas, and what it does not give you is a way to put your own toolbar next to it and have that toolbar update as the user scrolls. PDFSlick supplies exactly that missing layer: a store of viewer and document state, and bindings that expose it in the idiom of each framework.

The packages are published on npm under the @pdfslick scope. The React binding is @pdfslick/react, the Solid one is @pdfslick/solid, and the core is @pdfslick/core, which is what the Svelte example uses and what the plain JavaScript case would use. The site is pdfslick.dev, with the documentation, the getting started pages and an examples gallery.

The primary language is TypeScript, the licence is MIT, the default branch is main, and the repository is not archived. The last push was on 2026-10-09, the same day as the 4.0.3 release, with 4.0.2 in August 2026 and 4.0.1 at the start of that month.

## The React binding is one hook and three returned values

The React section starts with the install and then a single hook, and the shape of the example is what most people need.

```shell
npm install @pdfslick/react
```

with the yarn and pnpm equivalents commented underneath. Then the import, and the note that the stylesheet has to be included once, which is a step people skip:

```jsx
import { usePDFSlick } from "@pdfslick/react";
import PDFNavigation from "./yourcomponents/PDFNavigation";

//
// It is required to include PDFSlick's CSS styles once
// you can do it in your main `App.tsx` for example
//
import "@pdfslick/react/dist/pdf_viewer.css";
```

The hook is called with the document path and an options object, and the example passes scaleValue: "page-fit" so the page fits the container. It returns an object with three things you use. PDFSlickViewer is the viewer component used for viewing the PDF document. viewerRef is the ref callback that is provided as a prop to the PDFSlickViewer component. And usePDFSlickStore enables using the PDFSlick store within your React components.

That last one is the interesting return value, because it is a selector hook in the Zustand style, so you read exactly the slice of state you care about and your component only re-renders when that slice changes:

```jsx
  const scale = usePDFSlickStore((s) => s.scale);
  const numPages = usePDFSlickStore((s) => s.numPages);
  const pageNumber = usePDFSlickStore((s) => s.pageNumber);
```

The store values update automatically, so a custom navigation component that receives the store as a prop stays in step with the document without any manual wiring.

## Solid and Svelte reach the same store differently

The other two frameworks are worth reading side by side with React, because they show the same design expressed in three idioms rather than one library with three names.

The Solid binding installs as @pdfslick/solid and uses the same usePDFSlick hook. The difference is in the returned store: it comes back as pdfSlickStore, destructured as store, and it is passed to the viewer and to your components as a plain object rather than being a selector hook. The example reads store.scale, store.pageNumber and store.numPages directly inside the JSX, which in Solid is fine because the reactive graph handles the dependency tracking.

The Svelte example is the most different, because it uses @pdfslick/core rather than a framework binding. The component declares a reference to the PDF viewer container and a reference to the pdfSlick instance, keeps the pieces of store state it cares about in local variables, and then in onMount it dynamically imports the library:

```html
  onMount(async () => {
    const { create, PDFSlick } = await import('@pdfslick/core');
```

The comment explains why: the loading is all happening on the client side, so it is only loaded there. That is a server rendering constraint, not a stylistic choice, and it is the detail to carry into your own Svelte application.

The Svelte documentation also shows that you can subscribe to a portion of, or the entire, PDFSlick store's state, which is the same granular subscription model the React selector hook offers.

## The store is the integration point

It is worth being explicit about why a store, rather than a set of callbacks or a viewer API, is the right shape for this problem.

The viewer has a lot of state: the current page, the total number of pages, the scale, the rotation, the loaded document. Any application that wants to show a page indicator, a zoom control, a thumbnail strip or a search box needs to read that state, and it needs to read it in a way that re-renders when it changes. Passing a viewer instance to your components and polling it is the old approach and it does not work well in any of these frameworks.

A reactive store solves it once, and every framework binding is then a thin adapter over the same store. Zustand is what makes that cheap, since it is a small library with a selector API that works in React and in Solid without a framework-specific reactive system.

The README's own comment on the React example says it plainly. Access the store with the usePDFSlickStore hook, you can pass it as a prop to other components such as a navigation component, and toolbars, sidebars and components which render thumbnails can use it to get and react to the PDF document's and viewer's properties and changes.

So the intended architecture is that PDFSlick owns the canvas and the document, and your application owns the interface around it. That is the design that makes it possible to build a reader that looks like your product rather than like PDF.js.

## A Turborepo monorepo with changesets

The repository is a monorepo, and the package manifest shows how it is organised and released.

The workspaces are apps/*, packages/* and config/*, which is the usual split: the published libraries in packages, the example applications in apps, and the shared tooling configuration in config. The build is driven by Turborepo, with turbo run build, turbo run dev and turbo run lint, and there is a turbo.json at the root. The format script is a single Prettier call across TypeScript, TSX and Markdown.

Releases go through changesets. There is a .changeset/ directory, the changeset script invokes the CLI, version-packages runs the build and then changeset version, and publish-packages runs changeset publish. That is the standard modern flow: each package declares its own version, a change file describes the change, and the release tooling computes the version bumps and the changelogs.

The remaining root tooling is familiar. There is an eslint config package, eslint-config-custom, referenced as a local file dependency along with a fast-glob config package, Prettier for formatting, npm-check-updates behind the update-deps script, which runs ncu across the workspaces, and Rollup as the only runtime dependency of the root package. There is a .nvmrc, a .eslintrc.js, a .github/ directory, and the .changeset/ directory again.

The engines field is the strictest thing in the file: node ^22.20.0 or ^24.15.0 or >=26, and npm >=11.13.0. That is a modern floor, and it is the kind of constraint worth checking before you add the library to a project with an older toolchain.

## Examples, site and release state

The examples are part of the repository rather than a separate site, and the README links to them by path, with the React examples under apps/web/examples and the SolidJS ones under apps/solidweb/src/examples.

That arrangement is the right one for a library with four consumer packages, because the examples double as the integration tests for each binding. When the React and Solid examples live in the same monorepo as the packages, a change to the store shape breaks an example in the same commit, and the documentation and the tests cannot drift apart the way they do when the examples live in a separate repository.

The site is at pdfslick.dev, with a documentation section that has per-framework pages, the React one and the Solid one at least, and an examples gallery. The site is published by a GitHub Actions workflow, the publish_site.yml workflow, which is visible in the badge at the top of the README.

The release state is healthy. Versions 4.0.1 on 2026-08-01, 4.0.2 on 2026-08-09 and 4.0.3 on 2026-10-09, with the last push on the same day as the newest one. The npm badge in the README tracks the @pdfslick/core package, and the release badge tracks the GitHub release. Two months and three patch releases inside a 4.0 line is a normal maintenance rhythm, and the changesets workflow is what makes publishing several coordinated packages from one commit tractable.

## Conclusion

Adopt pdfslick if you need a PDF viewer inside a React, Solid or Svelte application and want your own chrome around the canvas rather than the default PDF.js viewer UI. Do not adopt it for a plain JavaScript page without looking at the framework packages, and do not skip the CSS import, because the viewer renders without its styles if the stylesheet is missing. Before you start, check which package your framework needs, @pdfslick/react, @pdfslick/solid or @pdfslick/core, and note that the Svelte example loads the library on the client only, so the same care is needed for server rendering in your own app.

## FAQ

### What is pdfslick and what is it built on?

It is a TypeScript library for viewing and interacting with PDF documents in React, SolidJS, Svelte and JavaScript apps, built on top of Mozilla's PDF.js. It uses Zustand to provide a reactive store for the loaded documents, and it is MIT licensed.

### Which package do I install for React, Solid and Svelte?

The React binding is @pdfslick/react, the Solid binding is @pdfslick/solid, and the Svelte example uses @pdfslick/core, which is also the package for plain JavaScript use. Each is installed with npm install, and the README lists the yarn and pnpm equivalents.

### What does the usePDFSlick hook return?

It returns PDFSlickViewer, the viewer component, viewerRef, the ref callback passed as a prop to that component, and usePDFSlickStore, which lets your own components read and react to the store's state, for example the scale, the number of pages and the current page number.

### Is there anything I must remember to do besides installing pdfslick?

Yes, the CSS. The stylesheet has to be included once, for example with an import of @pdfslick/react/dist/pdf_viewer.css, and the Svelte example additionally loads the library only on the client side with a dynamic import inside onMount.

### How is pdfslick developed and released?

It is a Turborepo monorepo with workspaces for the apps, the packages and the shared configuration, and releases go through changesets with separate version and publish scripts. The engine requirement is node ^22.20.0 or ^24.15.0 or >=26 with npm >=11.13.0, and the recent releases are 4.0.1, 4.0.2 and 4.0.3 through 2026-10-09.

## Sources

- [License: MIT](https://github.com/pdfslick/pdfslick/blob/main/LICENSE)
- [pdfslick/pdfslick on GitHub](https://github.com/pdfslick/pdfslick)
- [Project website](https://pdfslick.dev)
- [README](https://github.com/pdfslick/pdfslick/blob/main/README.md)
- [Releases](https://github.com/pdfslick/pdfslick/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pdfslick-pdfslick
