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

@edgepdf/viewer-react

v0.3.2

Published

EdgePDF Viewer - React components for viewing PDF documents with interactive markers and zoom controls

Downloads

329

Readme

@edgepdf/viewer-react

React components and hooks for the EdgePDF viewer. Provides a declarative, React-friendly API for viewing large PDF documents with interactive markers, annotations, and zoom controls.

Installation

npm install @edgepdf/viewer-react @edgepdf/viewer-js
# or
pnpm add @edgepdf/viewer-react @edgepdf/viewer-js
# or
yarn add @edgepdf/viewer-react @edgepdf/viewer-js

Quick Start

Next.js

In your Next.js application, you need to import the CSS file manually:

App Router (app directory):

// app/layout.tsx
import '@edgepdf/viewer-react/styles.css';
import { ViewerProvider } from '@edgepdf/viewer-react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <ViewerProvider>{children}</ViewerProvider>
      </body>
    </html>
  );
}
// app/page.tsx
import { EdgePDFViewer } from '@edgepdf/viewer-react';
import type { ViewerConfig } from '@edgepdf/types';

const config: ViewerConfig = {
  tileUrl: 'https://example.com/tiles/{z}/{x}/{y}.png',
  imageInfo: {
    width: 2000,
    height: 3000,
    tileSize: 256,
    maxZoom: 5,
    minZoom: 0,
  },
};

export default function Page() {
  return <EdgePDFViewer config={config} />;
}

Pages Router:

// pages/your-page.tsx
import '@edgepdf/viewer-react/styles.css';
import { ViewerProvider, EdgePDFViewer } from '@edgepdf/viewer-react';
import type { ViewerConfig } from '@edgepdf/types';

const config: ViewerConfig = {
  tileUrl: 'https://example.com/tiles/{z}/{x}/{y}.png',
  imageInfo: {
    width: 2000,
    height: 3000,
    tileSize: 256,
    maxZoom: 5,
    minZoom: 0,
  },
};

export default function Page() {
  return (
    <ViewerProvider>
      <EdgePDFViewer config={config} />
    </ViewerProvider>
  );
}

Other React Applications

import '@edgepdf/viewer-react/styles.css';
import {
  ViewerProvider,
  EdgePDFViewer,
  useMarkers,
  useViewer,
  useZoom,
} from '@edgepdf/viewer-react';
import type { ViewerConfig } from '@edgepdf/types';

const config: ViewerConfig = {
  tileUrl: 'https://example.com/tiles/{z}/{x}/{y}.png',
  imageInfo: {
    width: 2000,
    height: 3000,
    tileSize: 256,
    maxZoom: 5,
    minZoom: 0,
  },
};

function MarkerPanel() {
  const { markers, addMarker, removeMarker } = useMarkers();
  const { viewer, isInitialized } = useViewer();
  const { zoomIn, zoomOut, currentZoom } = useZoom();

  return (
    <div>
      <div>Initialized: {isInitialized ? 'yes' : 'no'}</div>
      <div>Total markers: {markers.length}</div>
      <div>Current zoom: {currentZoom}</div>
      <button onClick={zoomIn}>Zoom In</button>
      <button onClick={zoomOut}>Zoom Out</button>
    </div>
  );
}

function App() {
  return (
    <ViewerProvider>
      <MarkerPanel />
      <EdgePDFViewer config={config} />
    </ViewerProvider>
  );
}

API Reference

Components

<ViewerProvider>

Provides viewer context to child components. Must wrap all components that use viewer hooks.

Props:

interface ViewerProviderProps {
  children: ReactNode;
}

Example:

<ViewerProvider>
  <App />
</ViewerProvider>

<EdgePDFViewer>

Main React component for the EdgePDF viewer. Must be used within a ViewerProvider.

Props:

interface EdgePDFViewerProps {
  /** Viewer configuration */
  config: ViewerConfig;
  /** Optional map options */
  mapOptions?: MapOptions;
  /** Optional className for the container */
  className?: string;
  /** Optional style for the container */
  style?: React.CSSProperties;
  /** Show zoom controls (default: true) */
  showZoomControls?: boolean;
  /** Zoom controls position (only used if showZoomControls is true) */
  zoomControlsPosition?:
    | 'top-left'
    | 'top-right'
    | 'bottom-left'
    | 'bottom-right';
  /** Show zoom level in zoom controls (only used if showZoomControls is true) */
  showZoomLevel?: boolean;
  /** Enable annotation functionality (default: true) */
  enableAnnotation?: boolean;
  /** Show edit button in marker action controls (default: true) */
  showEditButton?: boolean;
  /** Show delete button in marker action controls (default: true) */
  showDeleteButton?: boolean;
  /** Default zoom level for initial view */
  defaultZoomLevel?: number;
  /** Callback when markers/pins are updated */
  onPinsUpdate?: (pins: Marker[]) => void;
  /** Callback when a marker is clicked */
  onMarkerClick?: (marker: Marker) => void;
  /** Callback when a marker is updated */
  onMarkerUpdate?: (marker: Marker) => void;
  /** Callback when a marker is deleted */
  onMarkerDelete?: (marker: Marker) => void;
  /** Callback when a marker is added (created by user tap/click) */
  onMarkerAdd?: (marker: Marker) => void;
  /** Default pins to preload (uses internal import) */
  defaultPins?: Marker[] | MarkerData;
  /** Children components */
  children?: ReactNode;
}

Example:

<EdgePDFViewer
  config={config}
  showZoomControls={true}
  zoomControlsPosition="top-right"
  enableAnnotation={true}
  onMarkerClick={(marker) => console.log('Marker clicked:', marker)}
  onPinsUpdate={(pins) => console.log('Pins updated:', pins)}
/>

<ZoomControls>

React component for zoom controls. Uses the JS library's zoom controls internally.

Props:

interface ZoomControlsProps {
  /** Optional className for the container */
  className?: string;
  /** Optional style for the container */
  style?: React.CSSProperties;
  /** Position of the controls */
  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
  /** Show zoom level display */
  showZoomLevel?: boolean;
}

Example:

<EdgePDFViewer config={config}>
  <ZoomControls position="top-right" showZoomLevel={true} />
</EdgePDFViewer>

Hooks

useViewer()

Hook to access and interact with the viewer instance.

Returns:

interface UseViewerReturn {
  /** Viewer instance */
  viewer: EdgePdfViewer | null;
  /** Whether viewer is initialized */
  isInitialized: boolean;
  /** Get the map instance */
  getMap: () => L.Map | null;
  /** Check if viewer is initialized */
  checkInitialized: () => boolean;
}

Example:

function MyComponent() {
  const { viewer, isInitialized, getMap } = useViewer();

  useEffect(() => {
    if (isInitialized && viewer) {
      const map = getMap();
      // Use map...
    }
  }, [isInitialized, viewer, getMap]);

  return <div>Viewer is {isInitialized ? 'ready' : 'loading'}</div>;
}

useMarkers()

Hook to manage markers in the viewer.

Returns:

interface UseMarkersReturn {
  /** Current markers */
  markers: Marker[];
  /** Add a new marker */
  addMarker: (options: CreateMarkerOptions) => Marker | null;
  /** Remove a marker by ID */
  removeMarker: (id: string) => boolean;
  /** Update a marker */
  updateMarker: (id: string, updates: Partial<Marker>) => boolean;
  /** Get a marker by ID */
  getMarker: (id: string) => Marker | null;
  /** Get all markers */
  getAllMarkers: () => Marker[];
  /** Export markers as JSON */
  exportMarkers: () => MarkerData;
  /** Import markers from JSON */
  importMarkers: (data: MarkerData) => boolean;
  /** Clear all markers */
  clearMarkers: () => void;
  /** Focus on a marker by panning and zooming to its position */
  focusMarker: (
    markerOrId: string | Marker,
    options?: {
      zoom?: number;
      animate?: boolean;
      duration?: number;
      offsetLeft?: number;
      offsetRight?: number;
      offsetTop?: number;
      offsetBottom?: number;
    }
  ) => boolean;
}

Example:

function MarkerControls() {
  const {
    markers,
    addMarker,
    removeMarker,
    updateMarker,
    exportMarkers,
    importMarkers,
    clearMarkers,
    focusMarker,
  } = useMarkers();

  const handleAddMarker = () => {
    addMarker({
      position: [100, 200],
      x: 100,
      y: 200,
      zoom: 1,
      label: 'New Marker',
    });
  };

  const handleExport = () => {
    const data = exportMarkers();
    console.log(JSON.stringify(data, null, 2));
  };

  const handleFocus = (markerId: string) => {
    focusMarker(markerId, { zoom: 3, animate: true });
  };

  return (
    <div>
      <button onClick={handleAddMarker}>Add Marker</button>
      <button onClick={handleExport}>Export Markers</button>
      <button onClick={clearMarkers}>Clear All</button>
      {markers.map((marker) => (
        <div key={marker.id}>
          {marker.label}
          <button onClick={() => removeMarker(marker.id)}>Remove</button>
          <button onClick={() => handleFocus(marker.id)}>Focus</button>
        </div>
      ))}
    </div>
  );
}

Methods:

  • addMarker(options: CreateMarkerOptions): Marker | null - Creates a new marker. Returns the created marker or null if viewer is not initialized.

  • removeMarker(id: string): boolean - Removes a marker by ID. Returns true if successful, false otherwise.

  • updateMarker(id: string, updates: Partial<Marker>): boolean - Updates a marker's properties. Returns true if successful, false otherwise.

  • getMarker(id: string): Marker | null - Gets a marker by ID. Returns the marker or null if not found.

  • getAllMarkers(): Marker[] - Gets all markers. Returns an array of all markers.

  • exportMarkers(): MarkerData - Exports all markers as JSON data. Returns a MarkerData object.

  • importMarkers(data: MarkerData): boolean - Imports markers from JSON data. Returns true if successful, false otherwise.

  • clearMarkers(): void - Removes all markers.

  • focusMarker(markerOrId: string | Marker, options?: FocusOptions): boolean - Focuses on a marker by panning and zooming. Returns true if successful, false otherwise.

    Options:

    • zoom?: number - Target zoom level (uses marker's zoom or default if not provided)
    • animate?: boolean - Whether to animate the transition (default: true)
    • duration?: number - Animation duration in seconds (default: 0.5)
    • offsetLeft?: number - Pixel offset to move focus marker to the left (default: 0)
    • offsetRight?: number - Pixel offset to move focus marker to the right (default: 0)
    • offsetTop?: number - Pixel offset to move focus marker upward (default: 0)
    • offsetBottom?: number - Pixel offset to move focus marker downward (default: 0)

    Example:

    // Focus with offset to account for overlay
    focusMarker(markerId, { offsetLeft: 100, offsetTop: 50 });

useZoom()

Hook to manage zoom in the viewer.

Returns:

interface UseZoomReturn {
  /** Current zoom state */
  zoomState: ZoomState | null;
  /** Current zoom level */
  currentZoom: number;
  /** Minimum zoom level */
  minZoom: number;
  /** Maximum zoom level */
  maxZoom: number;
  /** Zoom in */
  zoomIn: () => void;
  /** Zoom out */
  zoomOut: () => void;
  /** Set zoom level */
  setZoom: (zoom: number) => void;
  /** Check if can zoom in */
  canZoomIn: () => boolean;
  /** Check if can zoom out */
  canZoomOut: () => boolean;
}

Example:

function ZoomControls() {
  const {
    zoomIn,
    zoomOut,
    setZoom,
    currentZoom,
    minZoom,
    maxZoom,
    canZoomIn,
    canZoomOut,
  } = useZoom();

  return (
    <div>
      <button onClick={zoomOut} disabled={!canZoomOut()}>
        Zoom Out
      </button>
      <span>
        Zoom: {currentZoom} ({minZoom}-{maxZoom})
      </span>
      <button onClick={zoomIn} disabled={!canZoomIn()}>
        Zoom In
      </button>
      <button onClick={() => setZoom(2)}>Set Zoom to 2</button>
    </div>
  );
}

Methods:

  • zoomIn(): void - Zooms in by one level.

  • zoomOut(): void - Zooms out by one level.

  • setZoom(zoom: number): void - Sets a specific zoom level.

  • canZoomIn(): boolean - Checks if can zoom in. Returns true if current zoom is less than max zoom.

  • canZoomOut(): boolean - Checks if can zoom out. Returns true if current zoom is greater than min zoom.


useViewerContext()

Hook to access viewer context directly. Usually you should use useViewer(), useMarkers(), or useZoom() instead.

Returns:

interface ViewerContextValue {
  /** Viewer instance */
  viewer: EdgePdfViewer | null;
  /** Whether viewer is initialized */
  isInitialized: boolean;
  /** Current markers */
  markers: Marker[];
  /** Current zoom state */
  zoomState: ZoomState | null;
  /** Internal: Function to update context value */
  setContextValue: (
    updates: Partial<Omit<ViewerContextValue, 'setContextValue'>>
  ) => void;
}

Throws:

  • Error if used outside ViewerProvider

Example:

function MyComponent() {
  const { viewer, isInitialized, markers, zoomState } = useViewerContext();
  // Use context values...
}

Type Exports

The library also exports TypeScript types for convenience:

import type {
  EdgePDFViewerProps,
  ZoomControlsProps,
  ViewerContextValue,
  ViewerProviderProps,
  UseViewerReturn,
  UseMarkersReturn,
  UseZoomReturn,
} from '@edgepdf/viewer-react';

Examples

Basic Usage with Markers

import {
  ViewerProvider,
  EdgePDFViewer,
  useMarkers,
} from '@edgepdf/viewer-react';

function MarkerList() {
  const { markers, addMarker, removeMarker } = useMarkers();

  return (
    <div>
      <h3>Markers ({markers.length})</h3>
      {markers.map((marker) => (
        <div key={marker.id}>
          <strong>{marker.label}</strong>
          <button onClick={() => removeMarker(marker.id)}>Remove</button>
        </div>
      ))}
    </div>
  );
}

function App() {
  const config = {
    tileUrl: 'https://example.com/tiles/{z}/{x}/{y}.png',
    imageInfo: {
      width: 2000,
      height: 3000,
      tileSize: 256,
      maxZoom: 5,
      minZoom: 0,
    },
  };

  return (
    <ViewerProvider>
      <div style={{ display: 'flex' }}>
        <div style={{ width: '300px' }}>
          <MarkerList />
        </div>
        <div style={{ flex: 1, height: '100vh' }}>
          <EdgePDFViewer config={config} />
        </div>
      </div>
    </ViewerProvider>
  );
}

With Callbacks

function App() {
  const config = {
    tileUrl: 'https://example.com/tiles/{z}/{x}/{y}.png',
    imageInfo: {
      width: 2000,
      height: 3000,
      tileSize: 256,
      maxZoom: 5,
      minZoom: 0,
    },
  };

  const handleMarkerClick = (marker: Marker) => {
    console.log('Marker clicked:', marker);
  };

  const handlePinsUpdate = (pins: Marker[]) => {
    console.log('Pins updated:', pins);
    // Save to backend, localStorage, etc.
  };

  return (
    <ViewerProvider>
      <EdgePDFViewer
        config={config}
        onMarkerClick={handleMarkerClick}
        onPinsUpdate={handlePinsUpdate}
        enableAnnotation={true}
      />
    </ViewerProvider>
  );
}

Custom Zoom Controls

function CustomZoomControls() {
  const { zoomIn, zoomOut, currentZoom, canZoomIn, canZoomOut } = useZoom();

  return (
    <div style={{ position: 'absolute', top: 10, right: 10, zIndex: 1000 }}>
      <button onClick={zoomOut} disabled={!canZoomOut()}>
        −
      </button>
      <span>{currentZoom}</span>
      <button onClick={zoomIn} disabled={!canZoomIn()}>
        +
      </button>
    </div>
  );
}

function App() {
  return (
    <ViewerProvider>
      <EdgePDFViewer config={config} showZoomControls={false}>
        <CustomZoomControls />
      </EdgePDFViewer>
    </ViewerProvider>
  );
}

Import/Export Markers

function MarkerManager() {
  const { markers, exportMarkers, importMarkers, clearMarkers } = useMarkers();

  const handleExport = () => {
    const data = exportMarkers();
    const blob = new Blob([JSON.stringify(data, null, 2)], {
      type: 'application/json',
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'markers.json';
    a.click();
    URL.revokeObjectURL(url);
  };

  const handleImport = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const data = JSON.parse(e.target?.result as string);
        importMarkers(data);
      } catch (error) {
        console.error('Failed to import markers:', error);
      }
    };
    reader.readAsText(file);
  };

  return (
    <div>
      <button onClick={handleExport}>Export Markers</button>
      <input type="file" accept=".json" onChange={handleImport} />
      <button onClick={clearMarkers}>Clear All</button>
      <div>Total markers: {markers.length}</div>
    </div>
  );
}