React + Next.js adapter for microscope-js. One
<Viewer />component, oneuseViewer()hook for fully custom UIs, oneuseRegistry()helper. SSR-safe — works inside the Next.js App Router with'use client'.
pnpm add @microscope-js/react @microscope-js/core
# plus whichever format renderers you actually need
pnpm add @microscope-js/renderer-pdf @microscope-js/renderer-image
Peer deps: react >= 18, react-dom >= 18.
'use client';
import { Viewer, useRegistry } from '@microscope-js/react';
import { pdfRenderer } from '@microscope-js/renderer-pdf';
import { imageRenderer } from '@microscope-js/renderer-image';
export default function FilePreview({ file }: { file: File }) {
const registry = useRegistry([pdfRenderer, imageRenderer]);
return (
<Viewer
source={file}
registry={registry}
style={{ height: 600 }}
loadingFallback={<Spinner />}
errorFallback={(err) => <Alert>{err.message}</Alert>}
emptyFallback={<DropHint />}
/>
);
}
source accepts a File, Blob, ArrayBuffer, Uint8Array, URL, or string URL.
When you want full control over the toolbar / chrome around the viewer:
'use client';
import { useViewer, useRegistry } from '@microscope-js/react';
import { pdfRenderer } from '@microscope-js/renderer-pdf';
export function MyPdfViewer({ file }: { file: File }) {
const registry = useRegistry([pdfRenderer]);
const { containerRef, loading, error, handle } = useViewer({ source: file, registry });
const pageCount = handle?.capabilities?.pageCount as number | undefined;
return (
<div className="my-frame">
{loading && <Spinner />}
{error && <Alert>{error.message}</Alert>}
{pageCount && <span>{pageCount} pages</span>}
<div ref={containerRef} className="my-canvas" />
</div>
);
}
<Viewer /> props| Prop | Type | Description |
|---|---|---|
source |
Source | null | undefined |
What to render. null shows the emptyFallback. |
registry |
Registry |
From useRegistry([...]) or createRegistry([...]). |
rendererId? |
string |
Force-pick a renderer by id (skips matching). |
options? |
Record<string, unknown> |
Forwarded to the matched renderer. |
className? |
string |
Container class. |
style? |
CSSProperties |
Container inline style. Default is a 16:9 box with a subtle border. |
loadingFallback? |
ReactNode |
Shown while a render is in flight. |
errorFallback? |
(err) => ReactNode |
Render prop for errors. |
emptyFallback? |
ReactNode |
Shown when source is nullish. |
The adapter is SSR-safe — on the server it returns the wrapper div and nothing else. The format-specific renderer is only loaded inside useEffect. You can use it in two ways:
'use client' (recommended — keeps it simple)import dynamic from 'next/dynamic' and load with { ssr: false }pdfjs-dist, mammoth, xlsx, and jszip are loaded via import() so they never end up in the SSR bundle.
useViewer cancels the previous in-flight render and tears down the previous handle whenever source, registry, rendererId, or options change. Swapping files is safe and leak-free; object URLs are revoked on teardown.
@microscope-js/core — the framework-agnostic foundation