Custom rendering modes and context hooks in react-pdf
Table of contents
react-pdf features: using render modes for custom page output, accessing PDF state with useDocumentContext and usePageContext hooks, and working with structure tree accessibility data and ref forwarding.
renderModeon<Page>switches between"canvas"(default),"custom"(with acustomRenderer), and"none"(skips canvas paint — useful for headless text extraction).react-pdfexports three context hooks —useDocumentContext,usePageContext, anduseOutlineContext— for components nested inside<Document>,<Page>, or<Outline>.inputRefforwards a ref to the root<div>;canvasRefexposes the rendered<canvas>directly for things like exporting to a data URL.- Structure tree data (semantic page content for accessibility) is available via
onGetStructTreeSuccessafter the page renders.
react-pdf supports custom rendering for cases where the default canvas output isn’t sufficient. It also exports context hooks for building custom child components.
Render modes
The renderMode prop controls how a page is visually rendered:
| Mode | Description |
|---|---|
"canvas" | Default. Renders to an HTML <canvas> element |
"custom" | Uses your customRenderer component |
"none" | No visual rendering (useful for data extraction only) |
Setting render mode
Per-page:
<Page pageNumber={1} renderMode="custom" customRenderer={MyRenderer} />Document-wide (applies to all Page and Thumbnail children):
<Document file={file} renderMode="canvas"> <Page pageNumber={1} /></Document>Custom renderer
When renderMode="custom", you must provide a customRenderer component:
function MyCustomRenderer() { // Access page context for rendering data. const pageContext = usePageContext();
return ( <div className="custom-page"> {/* Your custom rendering logic. */} </div> );}
// In your component tree:// <Page// pageNumber={1}// renderMode="custom"// customRenderer={MyCustomRenderer}// />The none mode
Use renderMode="none" when you only want data, not visual output:
// Extract text without rendering anything visible.<Page pageNumber={1} renderMode="none" renderTextLayer={false} renderAnnotationLayer={false} onGetTextSuccess={({ items }) => { const fullText = items.map((item) => item.str).join(" "); processText(fullText); }}/>This saves memory and CPU by skipping canvas rendering entirely.
Context hooks
react-pdf exports three hooks for building custom child components that need access to PDF state.
useDocumentContext
Access document-level state from any child of Document:
import { useDocumentContext } from "react-pdf";
function CustomComponent() { const documentContext = useDocumentContext(); // Access the loaded PDF object, callbacks, options, etc. return <div>...</div>;}
// Must be inside a `Document`:// <Document file={file}>// <CustomComponent />// </Document>usePageContext
Access page-level state from any child of Page:
import { usePageContext } from "react-pdf";
function PageOverlay() { const pageContext = usePageContext(); // Access page number, scale, rotation, viewport, etc. return <div className="overlay">...</div>;}
// In your component tree:// <Document file={file}>// <Page pageNumber={1}>// <PageOverlay />// </Page>// </Document>useOutlineContext
Access outline state from children of Outline:
import { useOutlineContext } from "react-pdf";
function CustomOutlineItem() { const outlineContext = useOutlineContext(); return <div>...</div>;}
// In your component tree:// <Document file={file}>// <Outline>// <CustomOutlineItem />// </Outline>// </Document>Structure tree callbacks
react-pdf exposes the PDF structure tree (accessibility data) via callbacks:
<Page pageNumber={1} onGetStructTreeSuccess={(structTree) => { // Structure tree data for accessibility. console.log(structTree); }} onGetStructTreeError={(error) => { console.error("Failed to get struct tree:", error); }}/>The structure tree contains semantic information about the page content (headings, paragraphs, lists, etc.).
Forwarding refs
Document, Page, Outline, and Thumbnail all support inputRef for forwarding a ref to their root <div>:
function MeasuredPage() { const pageRef = useRef(null);
return ( <Page pageNumber={1} inputRef={pageRef} onRenderSuccess={() => { const { width, height } = pageRef.current.getBoundingClientRect(); console.log(`Page rendered at ${width}x${height}`); }} /> );}Page also supports canvasRef for direct canvas access:
<Page pageNumber={1} canvasRef={(canvas) => { // Direct access to the rendered canvas element. if (canvas) { const dataUrl = canvas.toDataURL("image/jpeg"); // Use the canvas image... } }}/>Key points
renderModecontrols visual output:"canvas"(default),"custom", or"none"."none"mode is useful for text extraction without visual rendering.- Context hooks (
useDocumentContext,usePageContext,useOutlineContext) let you build custom child components. inputRefforwards to the root div, andcanvasRefforwards to the canvas element.- Structure tree data is available via
onGetStructTreeSuccessfor accessibility.
How Nutrient Web SDK handles this
Instead of the render modes, context hooks, and ref forwarding patterns shown above, Nutrient Web SDK provides a straightforward custom renderer API:
const instance = await NutrientViewer.load({ container: "#pdf-container", document: "document.pdf", customRenderers: { Annotation: ({ annotation }) => { // Only customize note annotations; fall back to default UI for everything else. if (!(annotation instanceof NutrientViewer.Annotations.NoteAnnotation)) { return null; } const node = document.createElement("div"); node.className = "custom-annotation"; node.textContent = `📝 ${annotation.text?.value ?? ""}`; return { node, append: false }; // append: false replaces the default appearance. }, },});There are no render modes to toggle, no context hooks to learn, and no ref forwarding patterns to manage. The Annotation renderer fires for every annotation type, so instanceof checks keep your custom UI scoped — return null to fall back to defaults. append: false (the default) replaces the built-in appearance; append: true adds your node alongside it.
Learn more about Nutrient Web SDK | Migration guide | Contact Sales
FAQ
renderMode="custom"?Use it when the default canvas output doesn’t fit — for example, to render pages as SVG, render them as plain HTML for full text reflow, or show a placeholder for virtualized lists. For everything else, "canvas" is faster and simpler.
renderMode="none" and skipping the page entirely?"none" still loads the page data (so callbacks like onGetTextSuccess and onGetAnnotationsSuccess fire), but it skips the canvas paint. It’s the right pick for headless text or metadata extraction. Not rendering a <Page> at all means no getPage() call and no callbacks.
react-pdf README?No — useDocumentContext, usePageContext, and useOutlineContext are exported from react-pdf but only appear in the source (packages/react-pdf/src/index.ts). They’re stable enough to use, but the only documentation is the type signatures — there are no narrative guides.
onGetStructTreeSuccess fire relative to other callbacks?It fires after the page renders. The rough order is onLoadSuccess (page object available) → onRenderSuccess (canvas painted) → onGetTextSuccess/onGetAnnotationsSuccess/onGetStructTreeSuccess (data callbacks resolve). Don’t rely on a strict order between the three data callbacks — they fire as their underlying promises settle.
inputRef and canvasRef?inputRef forwards to the root <div> that wraps the canvas, text layer, and annotation layer — use it for measuring or focus management. canvasRef exposes the rendered <canvas> element directly, which is what you want for toDataURL(), custom WebGL compositing, or pixel-level image processing.
Yes — onGetStructTreeSuccess receives a StructTreeNode tree describing tagged content (headings, paragraphs, lists, figures). Walk the tree to build alt-text overlays, screen reader-friendly outlines, or DOM mirrors of the page semantics.