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

@papyrus-sdk/ui-react-native

v0.2.42

Published

React Native UI components for Papyrus viewers.

Readme

Papyrus UI React Native

React Native UI components for Papyrus viewers.

Install

npm install @papyrus-sdk/ui-react-native @papyrus-sdk/engine-native @papyrus-sdk/core @papyrus-sdk/types react-native-gesture-handler react-native-reanimated@^3.19.5

@papyrus-sdk/core, @papyrus-sdk/types, react-native-gesture-handler, and react-native-reanimated (3.19.5+) are required peer dependencies.

Configure Reanimated 3 in the host app, with react-native-reanimated/plugin last in the Babel plugin list. The native iOS PDF annotation mode animates the Topbar and dock; it respects the system Reduce Motion setting. While drawing, Concluir/Done closes the ink session through the existing store/native flush flow. The first Back press finishes drawing without leaving the reader; the next press navigates back.

Wrap the app root with GestureHandlerRootView before rendering Papyrus components:

import { GestureHandlerRootView } from 'react-native-gesture-handler';

<GestureHandlerRootView style={{ flex: 1 }}>
  <App />
</GestureHandlerRootView>

For EPUB/TXT/CBZ previews on mobile:

npm install react-native-webview

MobileDocumentEngine recognizes .cbz as comic and renders it in the same WebView runtime used by the mobile reader: continuous scrolling, lazy page extraction, bounded object-URL cache, and on-demand page previews in the navigation sheet. CBR is intentionally kept out of the base mobile runtime; RAR support needs the optional libarchive worker/WASM payload.

CBR mobile support is experimental and requires @papyrus-sdk/engine-cbr-mobile. Its Android WebView flow is not a substitute for device validation on iOS or Android, especially for picker-provided content:// and file:// sources.

Usage

import { Viewer, CoverPreview } from '@papyrus-sdk/ui-react-native';
import { MobileDocumentEngine } from '@papyrus-sdk/engine-native';

const engine = new MobileDocumentEngine();
await engine.load({ type: 'pdf', source: { uri: 'https://example.com/book.pdf' } });

<Viewer engine={engine} />

<CoverPreview
  source={{ uri: 'https://example.com/book.epub' }}
  type="epub"
  style={{ width: 120, height: 180 }}
/>

Shared reader chrome

ReaderHeaderView, ReaderBottomControlsView, ReaderSettingsView, and ReaderContentsView are controlled presentation components. They accept the display state and callbacks as props and do not read a Papyrus store or create a document engine. Existing Topbar, BottomBar, SettingsSheet, and RightSheet remain Papyrus-backed compatibility components and reuse the same presentation layer for their mobile EPUB chrome.

Use these views when an application hosts a different reading engine but wants to keep Papyrus's reader controls. Keep engine state, capability checks, navigation, and modal sequencing in the host adapter:

import {
  ReaderHeaderView,
  ReaderBottomControlsView,
  ReaderSettingsView,
  ReaderContentsView,
} from '@papyrus-sdk/ui-react-native';

<ReaderHeaderView
  title={book.title}
  isDark={theme === 'dark'}
  leadingAccessory={<BackButton onPress={goBack} />}
  trailingAccessory={<SettingsButton onPress={openSettings} />}
/>

The bottom controls accept capability-filtered action lists and optional navigation callbacks. Settings expose only supplied theme/font choices and actions. Contents accept flattened entries with a depth value for hierarchy; an empty list renders the supplied empty message and leaves the sheet closable. Readium support should not expose annotation or freehand controls unless that engine implements those capabilities.

EPUB renderers can share the same localized text-selection actions through getReaderSelectionActionOptions(locale). The stable IDs are copy, highlight, underline, note, and consult; normalizeReaderSelectionActionId also accepts Papyrus's older comment and define IDs. A host can pass these options to a native selection menu and route the selected text/locator to its annotation store without translating one engine's locator into another format.

Topbar Customization

Topbar supports replacing the default "Papyrus" text and logo.

<Topbar
  engine={engine}
  title="My Reader"
  logo={<MyLogoIcon />}
  onLogoPress={() => navigation.goBack()}
  onOpenSettings={() => setSettingsOpen(true)}
/>
  • title: replaces the default brand text.
  • logo: custom logo node (can be icon, image, or even a Pressable).
  • onLogoPress: optional callback to make the logo area act like a button.

Icon Guidelines

  • UI icons for this package live in packages/ui-react-native/icons.tsx.
  • Prefer inline SVG paths implemented with react-native-svg instead of adding a new icon runtime dependency.
  • When a product icon does not already exist in the file, prefer using the outline SVG from Tabler Icons as the visual baseline and adapt it to the local IconProps pattern.
  • Keep icons stroke-based when possible, preserve color/strokeWidth as props, and simplify paths only when needed for better balance in mobile chrome.
  • If an icon is intentionally derived from Tabler, keep that shape direction consistent for nearby controls so the toolbar/bottom-bar language stays coherent.

Mobile Tuning Flags

Layout and performance options are available on Viewer and RightSheet:

<Viewer
  engine={engine}
  maxPageWidth={760}
  fitPageToViewportHeight
  virtualWindowSize={8}
  maxToRenderPerBatch={6}
  removeClippedSubviews
/>

<RightSheet engine={engine} thumbsInitialCount={4} />
  • virtualWindowSize (Viewer): FlatList window size. Default: 8.
  • maxPageWidth (Viewer): optional fit-zoom width limit in React Native points. A narrower page is centered in the viewport; pinch zoom may enlarge it. Omitted by default.
  • fitPageToViewportHeight (Viewer): opt-in page fitting to the measured viewer height while preserving each page's aspect ratio; pinch zoom may enlarge it. Defaults to false. This option is only supported by the React Native compatibility renderer; it is not applied by the WebView or dedicated Android PDF renderer.
  • maxToRenderPerBatch (Viewer): max items per render batch. Default: 6.
  • removeClippedSubviews (Viewer): detach offscreen rows. Default: false for Android viewerMode="compat" and true otherwise. An explicit value is always respected.
  • thumbsInitialCount (RightSheet): initial thumbnails rendered when opening pages tab. Default: 4.

Recommended starting points:

  • Mid-range Android + large docs (500+ pages): virtualWindowSize={6}, maxToRenderPerBatch={4}, thumbsInitialCount={3}.
  • High-end devices: keep defaults (8, 6, 4) and tune only if you observe jank or memory pressure.

Performance Tips (Mobile)

  • Keep Viewer mounted before calling engine.load(...) for EPUB/TXT, so the WebView runtime is already ready.
  • Prefer URI loads ({ uri }) for local files instead of pre-converting content to base64.
  • For long documents, reduce virtualWindowSize and maxToRenderPerBatch first; then tune thumbnail pre-render count.
  • On Android mid-tier devices (around 4 GB RAM), expect temporary memory spikes while parsing EPUB archives and generating first render.

Mobile Performance Baseline (Audit Step)

Enable runtime diagnostics before rendering the viewer:

(globalThis as any).__PAPYRUS_MOBILE_PERF__ = {
  enabled: true,
  sampleMemory: true,
};

With diagnostics enabled, the native viewer emits structured logs such as:

  • [Papyrus Perf][Viewer] document.ready with initial load time.
  • [Papyrus Perf][Viewer] scroll.* with frame-gap and dropped-frame estimates.
  • [Papyrus Perf][RightSheet] memory.thumbnails.* for memory snapshots when opening thumbnails.
  • [Papyrus Perf][CoreStore] setDocumentState.burst when state updates happen in bursts.

Recommended baseline run:

  1. Load a large PDF (1000+ pages).
  2. Record document.ready for initial load.
  3. Scroll quickly for 10-15 seconds and capture scroll.* logs.
  4. Open thumbnails and capture memory.thumbnails.*.
  5. Jump between distant pages and check for setDocumentState.burst.

Large Files (EPUB/PDF) Troubleshooting

If large files fail to load or crash on mobile:

  • Avoid RN bridge base64 conversion for large local files (OOM risk). Use { uri: 'file:///...' }.
  • EPUB on mobile WebView now fetches local URI content inside the runtime and opens from ArrayBuffer.
  • If you see timeout errors:
    • WebView engine request timeout for load: 180000ms
    • Runtime EPUB open/ready timeout: 180000ms
    • Runtime EPUB display timeout: 30000ms
  • For very large EPUBs, first-open can take longer on mid-tier Android. This is expected due to zip parsing and spine bootstrapping.

Native ink motion packaging check

Run pnpm --filter @papyrus-sdk/ui-react-native check:ink-worklets before publishing. This builds and verifies all three ink chrome callbacks through the Reanimated Babel plugin in both CJS and ESM artifacts. Keep explicit "worklet" directives: bundler aliases such as useAnimatedStyle2 are not automatically recognized by the plugin.