@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-jsQuick 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 ornullif viewer is not initialized.removeMarker(id: string): boolean- Removes a marker by ID. Returnstrueif successful,falseotherwise.updateMarker(id: string, updates: Partial<Marker>): boolean- Updates a marker's properties. Returnstrueif successful,falseotherwise.getMarker(id: string): Marker | null- Gets a marker by ID. Returns the marker ornullif not found.getAllMarkers(): Marker[]- Gets all markers. Returns an array of all markers.exportMarkers(): MarkerData- Exports all markers as JSON data. Returns aMarkerDataobject.importMarkers(data: MarkerData): boolean- Imports markers from JSON data. Returnstrueif successful,falseotherwise.clearMarkers(): void- Removes all markers.focusMarker(markerOrId: string | Marker, options?: FocusOptions): boolean- Focuses on a marker by panning and zooming. Returnstrueif successful,falseotherwise.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. Returnstrueif current zoom is less than max zoom.canZoomOut(): boolean- Checks if can zoom out. Returnstrueif 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:
Errorif used outsideViewerProvider
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>
);
}