This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /blog/react-pdf-custom-rendering-hooks.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Custom rendering modes and context hooks in react-pdf

Table of contents

    This guide covers advanced 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.
    Custom rendering modes and context hooks in react-pdf
    TL;DR
    • renderMode on <Page> switches between "canvas" (default), "custom" (with a customRenderer), and "none" (skips canvas paint — useful for headless text extraction).
    • react-pdf exports three context hooks — useDocumentContext, usePageContext, and useOutlineContext — for components nested inside <Document>, <Page>, or <Outline>.
    • inputRef forwards a ref to the root <div>; canvasRef exposes the rendered <canvas> directly for things like exporting to a data URL.
    • Structure tree data (semantic page content for accessibility) is available via onGetStructTreeSuccess after 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:

    ModeDescription
    "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

    • renderMode controls 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.
    • inputRef forwards to the root div, and canvasRef forwards to the canvas element.
    • Structure tree data is available via onGetStructTreeSuccess for 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

    When should I use 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.

    What’s the difference between 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.

    Are the context hooks documented in the 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.

    When does 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.

    What’s the difference between 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.

    Can I read PDF accessibility tags from the structure tree?

    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.

    Austin Nguyen

    Austin Nguyen

    AI Engineer

    When Austin isn’t pulling all-nighters to build new features, he enjoys watching science videos on YouTube and cooking.

    Explore related topics

    Try for free Ready to get started?