Display PDFs in your React app as easily as if they were images.
—
React hooks for accessing PDF context data and state within component trees, enabling custom components to integrate with the react-pdf ecosystem.
Provides access to document-level context data including PDF document proxy, link service, and page registration functions.
/**
* Hook to access document context data
* Must be used within a Document component or provider
* @returns Document context object or null if outside Document
*/
function useDocumentContext(): DocumentContextType;
interface DocumentContextType {
/** Loaded PDF document proxy */
pdf?: PDFDocumentProxy | false;
/** Link service for handling internal and external links */
linkService: LinkService;
/** Function to register page elements for navigation */
registerPage: (pageIndex: number, ref: HTMLDivElement) => void;
/** Function to unregister page elements */
unregisterPage: (pageIndex: number) => void;
/** Item click handler for navigation */
onItemClick?: (args: OnItemClickArgs) => void;
/** Image resources path for annotations */
imageResourcesPath?: string;
/** Document rendering mode */
renderMode?: RenderMode;
/** Document scale factor */
scale?: number;
/** Document rotation */
rotate?: number | null;
} | null;Usage Examples:
import { useDocumentContext } from "react-pdf";
// Custom component accessing document info
function DocumentInfo() {
const documentContext = useDocumentContext();
if (!documentContext?.pdf) {
return <div>No document loaded</div>;
}
const { pdf } = documentContext;
return (
<div className="document-info">
<h3>Document Information</h3>
<p>Total Pages: {pdf.numPages}</p>
<p>PDF.js Version: {pdf.pdfInfo?.PDFFormatVersion}</p>
</div>
);
}
// Custom navigation component
function CustomNavigation() {
const documentContext = useDocumentContext();
const [currentPage, setCurrentPage] = useState(1);
if (!documentContext?.pdf) return null;
const { pdf, onItemClick } = documentContext;
const handlePageChange = (pageNumber) => {
setCurrentPage(pageNumber);
if (onItemClick) {
onItemClick({
pageIndex: pageNumber - 1,
pageNumber: pageNumber
});
}
};
return (
<div className="custom-navigation">
<button
onClick={() => handlePageChange(Math.max(1, currentPage - 1))}
disabled={currentPage === 1}
>
Previous
</button>
<span>Page {currentPage} of {pdf.numPages}</span>
<button
onClick={() => handlePageChange(Math.min(pdf.numPages, currentPage + 1))}
disabled={currentPage === pdf.numPages}
>
Next
</button>
</div>
);
}
// Document metadata extractor
function DocumentMetadata() {
const documentContext = useDocumentContext();
const [metadata, setMetadata] = useState(null);
useEffect(() => {
if (documentContext?.pdf) {
documentContext.pdf.getMetadata().then(({ info, metadata }) => {
setMetadata({ info, metadata });
});
}
}, [documentContext?.pdf]);
if (!metadata) return <div>Loading metadata...</div>;
return (
<div className="metadata">
<h3>Document Metadata</h3>
<p>Title: {metadata.info.Title || 'Untitled'}</p>
<p>Author: {metadata.info.Author || 'Unknown'}</p>
<p>Subject: {metadata.info.Subject || 'None'}</p>
<p>Creator: {metadata.info.Creator || 'Unknown'}</p>
<p>Producer: {metadata.info.Producer || 'Unknown'}</p>
<p>Creation Date: {metadata.info.CreationDate || 'Unknown'}</p>
</div>
);
}Provides access to page-level context data including page proxy, dimensions, and rendering settings.
/**
* Hook to access page context data
* Must be used within a Page component or provider
* @returns Page context object or null if outside Page
*/
function usePageContext(): PageContextType;
interface PageContextType {
/** PDF page proxy object */
page: PDFPageProxy | false | undefined;
/** Zero-based page index */
pageIndex: number;
/** One-based page number */
pageNumber: number;
/** Page scale factor */
scale: number;
/** Page rotation in degrees */
rotate: number;
/** Canvas background color */
canvasBackground?: string;
/** Custom text renderer function */
customTextRenderer?: CustomTextRenderer;
/** Device pixel ratio */
devicePixelRatio?: number;
/** Whether forms should be rendered */
renderForms: boolean;
/** Whether text layer should be rendered */
renderTextLayer: boolean;
/** Internal CSS class name */
_className?: string;
} | null;Usage Examples:
import { usePageContext } from "react-pdf";
// Custom page overlay component
function PageOverlay() {
const pageContext = usePageContext();
if (!pageContext?.page) return null;
const { page, pageNumber, scale, rotate } = pageContext;
const viewport = page.getViewport({ scale, rotation: rotate });
return (
<div
className="page-overlay"
style={{
position: 'absolute',
top: 0,
left: 0,
width: viewport.width,
height: viewport.height,
pointerEvents: 'none',
border: '2px solid red',
zIndex: 10
}}
>
<div className="page-label">
Page {pageNumber}
</div>
</div>
);
}
// Page dimensions display
function PageDimensions() {
const pageContext = usePageContext();
if (!pageContext?.page) return null;
const { page, scale, rotate } = pageContext;
const viewport = page.getViewport({ scale: 1, rotation: rotate });
const scaledViewport = page.getViewport({ scale, rotation: rotate });
return (
<div className="page-dimensions">
<h4>Page Dimensions</h4>
<p>Original: {viewport.width.toFixed(1)} × {viewport.height.toFixed(1)}</p>
<p>Scaled: {scaledViewport.width.toFixed(1)} × {scaledViewport.height.toFixed(1)}</p>
<p>Scale: {(scale * 100).toFixed(0)}%</p>
<p>Rotation: {rotate}°</p>
</div>
);
}
// Custom text highlighter using page context
function TextHighlighter({ searchTerm }) {
const pageContext = usePageContext();
const [textItems, setTextItems] = useState([]);
useEffect(() => {
if (pageContext?.page) {
pageContext.page.getTextContent().then(textContent => {
setTextItems(textContent.items);
});
}
}, [pageContext?.page]);
if (!pageContext?.page || !searchTerm) return null;
const { page, scale, rotate } = pageContext;
const viewport = page.getViewport({ scale, rotation: rotate });
return (
<div className="text-highlights">
{textItems
.filter(item => item.str.toLowerCase().includes(searchTerm.toLowerCase()))
.map((item, index) => {
const [x, y, width, height] = item.transform;
return (
<div
key={index}
className="highlight"
style={{
position: 'absolute',
left: x * scale,
top: viewport.height - (y * scale),
width: item.width * scale,
height: item.height * scale,
backgroundColor: 'yellow',
opacity: 0.3,
pointerEvents: 'none'
}}
/>
);
})}
</div>
);
}Provides access to outline-specific context data for navigation and interaction handling.
/**
* Hook to access outline context data
* Must be used within an Outline component or provider
* @returns Outline context object or null if outside Outline
*/
function useOutlineContext(): OutlineContextType;
interface OutlineContextType {
/** Item click handler for outline navigation */
onItemClick?: (args: OnItemClickArgs) => void;
} | null;Usage Examples:
import { useOutlineContext } from "react-pdf";
// Custom outline item component
function CustomOutlineItem({ item, level = 0 }) {
const outlineContext = useOutlineContext();
const handleClick = () => {
if (outlineContext?.onItemClick && item.dest) {
// Note: In real implementation, you'd need to resolve dest to page number
outlineContext.onItemClick({
dest: item.dest,
pageIndex: 0, // Would need to resolve from dest
pageNumber: 1 // Would need to resolve from dest
});
}
};
return (
<div className="custom-outline-item">
<div
className={`outline-title level-${level}`}
onClick={handleClick}
style={{
paddingLeft: level * 20,
cursor: 'pointer',
fontWeight: item.bold ? 'bold' : 'normal',
fontStyle: item.italic ? 'italic' : 'normal',
color: item.color ? `rgb(${item.color.join(',')})` : 'inherit'
}}
>
{item.title}
</div>
{item.items && item.items.length > 0 && (
<div className="outline-children">
{item.items.map((child, index) => (
<CustomOutlineItem
key={index}
item={child}
level={level + 1}
/>
))}
</div>
)}
</div>
);
}
// Outline with custom interaction
function InteractiveOutline() {
const outlineContext = useOutlineContext();
const [expandedItems, setExpandedItems] = useState(new Set());
const toggleExpanded = (itemId) => {
const newExpanded = new Set(expandedItems);
if (newExpanded.has(itemId)) {
newExpanded.delete(itemId);
} else {
newExpanded.add(itemId);
}
setExpandedItems(newExpanded);
};
return (
<div className="interactive-outline">
{/* Custom outline rendering with expand/collapse */}
</div>
);
}Using multiple context hooks together for complex custom components.
interface CombinedContextUsage {
/** Using document and page contexts together */
documentAndPageContext?: () => void;
/** Using all available contexts */
allContexts?: () => void;
}Usage Examples:
// Component using both document and page contexts
function PageWithDocumentInfo() {
const documentContext = useDocumentContext();
const pageContext = usePageContext();
if (!documentContext?.pdf || !pageContext?.page) {
return <div>Loading...</div>;
}
const { pdf } = documentContext;
const { pageNumber, scale } = pageContext;
return (
<div className="page-with-info">
<div className="page-header">
<h3>Document: {pdf.numPages} pages</h3>
<p>Current: Page {pageNumber} at {(scale * 100).toFixed(0)}%</p>
</div>
{/* Page content would be rendered here */}
</div>
);
}
// Full-featured PDF viewer using all contexts
function FullFeaturedViewer() {
const documentContext = useDocumentContext();
const pageContext = usePageContext();
const outlineContext = useOutlineContext();
const [showMetadata, setShowMetadata] = useState(false);
const [showOutline, setShowOutline] = useState(false);
return (
<div className="full-viewer">
<div className="toolbar">
<button onClick={() => setShowMetadata(!showMetadata)}>
{showMetadata ? 'Hide' : 'Show'} Metadata
</button>
<button onClick={() => setShowOutline(!showOutline)}>
{showOutline ? 'Hide' : 'Show'} Outline
</button>
{pageContext && (
<span>
Page {pageContext.pageNumber} •
Scale {(pageContext.scale * 100).toFixed(0)}%
</span>
)}
</div>
<div className="viewer-content">
{showOutline && (
<div className="sidebar">
{/* Custom outline component */}
</div>
)}
<div className="main-content">
{showMetadata && (
<div className="metadata-panel">
{/* Document metadata */}
</div>
)}
{/* Page content */}
</div>
</div>
</div>
);
}
// Context validation helper
function useValidatedContexts() {
const documentContext = useDocumentContext();
const pageContext = usePageContext();
const isDocumentReady = Boolean(documentContext?.pdf);
const isPageReady = Boolean(pageContext?.page);
return {
documentContext,
pageContext,
isDocumentReady,
isPageReady,
isFullyReady: isDocumentReady && isPageReady
};
}
function ContextAwareComponent() {
const {
documentContext,
pageContext,
isFullyReady
} = useValidatedContexts();
if (!isFullyReady) {
return <div>Contexts not ready</div>;
}
return (
<div>
{/* Component that requires both contexts */}
</div>
);
}Proper error handling and validation when using context hooks.
interface ContextErrorHandling {
/** Validate context availability */
validateContext?: () => boolean;
/** Handle missing context gracefully */
handleMissingContext?: () => React.ReactElement;
}Usage Examples:
// Context validation wrapper
function withContextValidation(Component, requiredContexts = []) {
return function ValidatedComponent(props) {
const documentContext = useDocumentContext();
const pageContext = usePageContext();
const outlineContext = useOutlineContext();
const contexts = {
document: documentContext,
page: pageContext,
outline: outlineContext
};
// Check if all required contexts are available
const missingContexts = requiredContexts.filter(
contextName => !contexts[contextName]
);
if (missingContexts.length > 0) {
console.warn(
`Component requires ${requiredContexts.join(', ')} context(s), ` +
`but ${missingContexts.join(', ')} ${missingContexts.length === 1 ? 'is' : 'are'} missing`
);
return <div>Missing required PDF context</div>;
}
return <Component {...props} />;
};
}
// Usage
const ValidatedPageInfo = withContextValidation(
function PageInfo() {
const pageContext = usePageContext();
return <div>Page {pageContext.pageNumber}</div>;
},
['page']
);
// Safe context hook with fallbacks
function useSafeDocumentContext() {
const context = useDocumentContext();
return {
pdf: context?.pdf || null,
numPages: context?.pdf?.numPages || 0,
isReady: Boolean(context?.pdf),
linkService: context?.linkService || null,
registerPage: context?.registerPage || (() => {}),
unregisterPage: context?.unregisterPage || (() => {})
};
}
function SafeDocumentInfo() {
const { pdf, numPages, isReady } = useSafeDocumentContext();
if (!isReady) {
return <div>Document not ready</div>;
}
return <div>Document has {numPages} pages</div>;
}Install with Tessl CLI
npx tessl i tessl/npm-react-pdf