npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@n-uf/papyrus-react

v26.11.3

Published

React components for the papyrus documents engine: content-free page and content blocks, the React-typed document registry and print styles

Readme

@n-uf/papyrus-react

React components for the @n-uf/papyrus documents engine: content-free building blocks for documents rendered as HTML and exported as A4 PDF. It contains no brand, copy or document content; the documents package supplies those.

[!CAUTION]

Not yet stable

@n-uf/papyrus-react is young and still changing. Its components, props and print styling are not final.

Version numbers are dates (YY.M.R), not semantic versions, so any release may break things, including component props, the print styles and the Tailwind stylesheet. Pin an exact version and read the CHANGELOG before you upgrade.

Ships TypeScript source (exports point at ./src); list it in a Next.js app's transpilePackages.

pnpm add @n-uf/papyrus-react

| Import | What | | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | @n-uf/papyrus-react | everything below, re-exported, plus the engine's page contract and sheets | | /layout, /typography, /containers, /cards, /tables, /lists, /indicator, /logo, /file-list | page and content blocks (PDFPage, Logo, DocumentFooter, TOCItem, ...) | | /dossier | the engine's document registry typed for React (registerDoc, docRegistry), DocPageProps, and viewer helpers (DocPageSlice, useDocPdf, DocPrintView) | | /params | useDocParam: a document parameter synced with the URL | | /styles | printStyles (A4 sheet CSS + print fixes for these blocks, as a CSS string) | | /tailwind.css | Tailwind v4 source stylesheet: @import "@n-uf/papyrus-react/tailwind.css" |

The PDF exporter, the request handler and the Vite plugin are in @n-uf/papyrus (/pdf, /server, /vite).

Branding

Logo and DocumentFooter render only what you pass:

<Logo name={<MyWordmark />} variantTextLines={{ home: ["MADE BY"] }} />
<DocumentFooter title="Report" copyright="© 2030 Acme" />
<TOCItem number="1" title="Intro" page="3" section="market" sectionColors={{ market: "rgb(14, 116, 144)" }} />

Viewing registered documents

@n-uf/papyrus-react/dossier also holds what a document viewer needs around a registered document, so the viewer carries no document plumbing of its own:

| Export | What | | --- | --- | | DocPageSlice | page N (1-based) of a document at its natural size, DOC_PAGE_SIZE (A4 at 96 dpi), without the grey sheet-stack framing | | useDocPdf(docId, options?) | { available, state, render } for the exported PDF at <basePath>/<id>.pdf | | DocPrintView | the markup papyrus pdf checks (data-papyrus-doc, data-papyrus-page-count) around the document |

import { DOC_PAGE_SIZE, DocPageSlice, DocPrintView, docRegistry, useDocPdf } from "@n-uf/papyrus-react/dossier";
import { printStyles } from "@n-uf/papyrus-react/styles";

// Print route: what the PDF export opens
const doc = docRegistry.get(docId);
if (doc && print) return <DocPrintView doc={doc} />;

// A thumbnail of page 2, scaled by the host to 200 px wide
const scale = 200 / DOC_PAGE_SIZE.width;
<div style={{ width: 200, height: DOC_PAGE_SIZE.height * scale, overflow: "hidden", position: "relative" }}>
  <DocPageSlice doc={doc} page={2} style={{ position: "absolute", transform: `scale(${scale})`, transformOrigin: "0 0" }} />
</div>;

// The PDF: a committed file when `available` (a plain link), else one printed on request with the
// reader's document parameters (a document whose values the reader sets, such as a calculator)
const pdf = useDocPdf(docId, { ignore: ["closed"], fileName: (id) => `acme-${id}.pdf` });
{pdf.available ? (
  <a href={`/docs/${docId}.pdf`} download={`acme-${docId}.pdf`}>Download PDF</a>
) : (
  <button disabled={pdf.state === "busy"} onClick={pdf.render}>
    {pdf.state === "busy" ? "Preparing PDF…" : pdf.state === "failed" ? "Try again" : "Download PDF"}
  </button>
)}
  • A slice renders the whole document component and shows only its Nth .pdf-page: interactive pages keep working and the layout is the one the PDF prints, but a document drawn as N slices renders N times. Scaling to a width is the host's job. The slice needs the documents' printStyles (documents inject them already): its screen rules, keyed on data-papyrus-slice, strip the sheet framing.
  • available is a HEAD request whose content type must be a PDF, because hosts such as Vercel answer unknown paths with index.html and status 200. render() requests <basePath>/<id>.pdf?<current query minus papyrus's own printand yourignore> and downloads the result; it is the way to a PDF for a document with no committed file (when available, link to the file). It never rejects (a failure sets state to "failed"), and its result is dropped after unmount or an id change; going back to a document starts from "idle".

Moving from @n-uf/papyrus 0.3

Up to 0.3, these components were part of @n-uf/papyrus. Replace @n-uf/papyrus with @n-uf/papyrus-react in component, /dossier, /params, /styles and /tailwind.css imports, and transpilePackages. Imports of @n-uf/papyrus/pdf, /server and /vite stay as they are.

Versions

Calendar versions YY.M.R, always the same as @n-uf/papyrus's (see its versioning policy). Releases up to 0.4.0 used semver.

License

PolyForm Perimeter 1.0.1, the same license as hypr-tiling; see LICENSE.