Customizing Toolbar
Overview of the Toolbar
React PDF features a flexible toolbar with RPDefaultLayout
component which can be configured to show or hide a top bar and a left sidebar. This provides an intuitive interface for navigating and interacting with PDF documents.
By default, all tools are enabled, giving users immediate access to the full range of features out-of-the-box without any additional configurations. However, React PDF also offers customization options, allowing you to tailor the toolbar to fit your specific use case.
Let’s take a closer look at the tutorial below to learn how to customize and adapt the toolbar to your needs.
To get started, explore the available configuration options in the RPDefault Layout Component.
Use Existing Slots
React PDF allows you to override the behavior of a tool in the toolbar to extend or modify each default functionality based on your application’s needs.
For a complete list of slots available for customizing tools, refer to the RPSlot - Tools documentation.
Example: Customizing the zoomTool
You can use the zoomTool
slot to customize how the zoom functionality behaves in the viewer. In this example, custom buttons are added to adjust the zoom scale. The zoom
function is used to increase or decrease the zoom level by a specified amount, and the currentScale is displayed in the middle.
import { useCallback } from 'react'import { RPConfig, RPProvider, RPDefaultLayout, RPPages,} from '@pdf-viewer/react'
const CustomZoomLayout = ({zoomLevel, setZoomLevel}) => {
// zoom in 10 and max 1000const handleZoomIn = useCallback(() => {setZoomLevel((prev) => Math.min(prev + 10, 1000))}, [setZoomLevel])
// zoom out 10 and min 10 to prevent scale to be less than 1const handleZoomOut = useCallback(() => {setZoomLevel((prev) => Math.max(prev - 10, 10))}, [setZoomLevel])
return (<div> <button onClick={handleZoomIn}> Zoom In </button> {zoomLevel} <button onClick={handleZoomOut}> Zoom Out </button></div>)}
export const AppPdfViewer = () => {return (<RPConfig> <RPProvider src="https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf"> <RPDefaultLayout style={{ width : "100%" , height: '550px' }} slots={{ zoomTool: CustomZoomLayout }} > <RPPages /> </RPDefaultLayout> </RPProvider></RPConfig>)}
import { useCallback, type FC } from 'react'import { RPConfig, RPProvider, RPDefaultLayout, RPPages, type ZoomProps} from '@pdf-viewer/react'
const CustomZoom : FC<ZoomProps> = ({zoomLevel, setZoomLevel}) => {
// zoom in 10 and max 1000const handleZoomIn = useCallback(() => {setZoomLevel((prev) => Math.min(prev + 10, 1000))}, [setZoomLevel])
// zoom out 10 and min 10 to prevent scale to be less than 1const handleZoomOut = useCallback(() => {setZoomLevel((prev) => Math.max(prev - 10, 10))}, [setZoomLevel])
return (<div> <button onClick={handleZoomIn}> Zoom In </button> {zoomLevel} <button onClick={handleZoomOut}> Zoom Out </button></div>)}
export const AppPdfViewer = () => {return (<RPConfig> <RPProvider src="https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf"> <RPDefaultLayout style={{ width : "100%" , height: '550px' }} slots={{ zoomTool: CustomZoom }} > <RPPages /> </RPDefaultLayout> </RPProvider></RPConfig>)}
- The Zoom Out button decreases the zoom level by 10 percent.
- The Zoom In button increases the zoom level by 10 percent.
- The zoom level value is displayed between the buttons to show the current zoom level.
Using Custom Buttons/Icons with React Hooks
Certain tools within the toolbar and More Options menu expose a React Context API
, enabling advanced control and customization. These APIs allow developers to interact with and modify the behavior of specific tools directly from the parent component.
The methods are exposed through React’s hooks
and can be accessed via a ref
in the parent component. This setup allows developers to dynamically trigger actions, such as adjusting zoom levels, toggling visibility, or navigating pages. For a detailed list of available configuration options, refer to the Hooks documentation.
Show Tool from ‘More Options’ in the Top Bar
By default, the tools available in the ‘More Options’ menu are not shown in the top bar. However, you can use the React PDF’s React Context API to selectively display specific tools directly in the top bar, providing a more tailored user experience.
Example: Displaying Rotate Tool on the Top Bar
By default, the Rotate Clockwise and Rotate Counterclockwise tools are located within the More Options menu. This example demonstrates how to use the React Context API to display these tools directly on the top bar for better accessibility.
Step 1: Creating button
Element
Start by creating two button elements using useMemo—one for rotating clockwise (↻) and one for counterclockwise (↺). This ensures the buttons are only created once and reused efficiently.
const buttonRotateClockwise = useMemo(() => {const button = document.createElement("button");button.innerHTML = "↻";return button;}, []);
const buttonRotateCounterclockwise = useMemo(() => {const button = document.createElement("button");button.innerHTML = "↺";return button;}, []);
const buttonRotateClockwise = useMemo(() => {const button = document.createElement("button");button.innerHTML = "↻";button.onclick = handleRotateClockwise;return button;}, []);
const buttonRotateCounterclockwise = useMemo(() => {const button = document.createElement("button");button.innerHTML = "↺";button.onclick = handleRotateCounterclockwise;return button;}, []);
Step 2: Create Rotation Handlers
Define two functions to handle rotation. Each updates the rotation state by 90 degrees—clockwise or counterclockwise—using useCallback to optimize performance.
// Rotate 90 degrees in a clockwise directionconst handleRotateClockwise = useCallback(() => {setRotate((prev) => (prev + 90) % 360)}, [setRotate])
// Rotate 90 degrees in a counterclockwise directionconst handleRotateCounterclockwise = useCallback(() => {setRotate((prev) => (prev - 90 + 360) % 360)}, [setRotate])
// Rotate 90 degrees in a clockwise directionconst handleRotateClockwise = useCallback(() => {setRotate((prev) => (prev + 90) % 360)}, [setRotate])
// Rotate 90 degrees in a counterclockwise directionconst handleRotateCounterclockwise = useCallback(() => {setRotate((prev) => (prev - 90 + 360) % 360)}, [setRotate])
Step 3: Add Buttons to the Top Bar
Use the handleLoaded function to insert your custom rotate buttons into the viewer’s top bar. This targets the top bar element and prepends both buttons after the viewer loads.
const handleLoaded = useCallback(() => {// Get the element of React PDF.const topBarRight = ref.current?.querySelector('[data-rp="topBarRight"]');// Add the button element to the top bar.topBarRight?.prepend(buttonRotateClockwise);topBarRight?.prepend(buttonRotateCounterclockwise);}, [buttonRotateClockwise]);
const handleLoaded = useCallback(() => {// Get the element of React PDF.const topBarRight = ref.current?.querySelector('[data-rp="topBarRight"]');// Add the button element to the top bar.topBarRight?.prepend(buttonRotateClockwise);topBarRight?.prepend(buttonRotateCounterclockwise);}, [buttonRotateClockwise]);
Step 4: Clean Up Buttons on Unmount
Define a cleanup function to remove the rotate buttons when the component unmounts. This prevents duplicate buttons if the viewer reloads or re-renders.
const cleanupOnLoaded = useCallback(() => {if (buttonRotateClockwise) { buttonRotateClockwise.remove();}if (buttonRotateCounterclockwise) { buttonRotateCounterclockwise.remove();}}, [buttonRotateClockwise, buttonRotateCounterclockwise]);
const cleanupOnLoaded = useCallback(() => {if (buttonRotateClockwise) { buttonRotateClockwise.remove();}if (buttonRotateCounterclockwise) { buttonRotateCounterclockwise.remove();}}, [buttonRotateClockwise, buttonRotateCounterclockwise]);
Step 5: Connect Buttons to the PDF Viewer
Pass the handleLoaded and cleanupOnLoaded functions to the viewer layout. This hooks your custom buttons into the viewer when it loads and removes them when it unmounts.
export const AppPdfViewer = () => { return ( <RPDefaultLayout ref={ref} onLoaded={handleLoaded} cleanupOnLoaded={cleanupOnLoaded} style={{ width : "100%" , height: '550px' }} > <RPPages /> </RPDefaultLayout> )}
export const AppPdfViewer : FC = () => { return ( <RPDefaultLayout ref={ref} onLoaded={handleLoaded} cleanupOnLoaded={cleanupOnLoaded} style={{ width : "100%" , height: '550px' }} > <RPPages /> </RPDefaultLayout> )}
Here are the full codes for this example:
// App.jsx (Parent Component)import { RPConfig, RPProvider } from "@pdf-viewer/react";import { AppPdfViewer } from "./components/AppPdfViewer";export default function App{ return ( <RPConfig> <RPProvider src="https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf"> <AppPdfViewer /> </RPProvider> </RPConfig> )}
// AppPdfViewer.jsx (Child Component)import React, { useCallback, useMemo, useRef } from "react";
export const AppPdfViewer = () => { const { rotate, setRotate } = useRotationContext(); const ref = useRef<HTMLDivElement>(null);
// Rotate 90 degrees in a clockwise direction const handleRotateClockwise = useCallback(() => { setRotate((prev) => (prev + 90) % 360); }, [setRotate]);
// Rotate 90 degrees in a counterclockwise direction const handleRotateCounterclockwise = useCallback(() => { setRotate((prev) => (prev - 90 + 360) % 360); }, [setRotate]);
// Create the button element. const buttonRotateClockwise = useMemo(() => { const button = document.createElement("button"); button.innerHTML = "↻"; button.onclick = handleRotateClockwise; return button; }, []);
const buttonRotateCounterclockwise = useMemo(() => { const button = document.createElement("button"); button.innerHTML = "↺"; button.onclick = handleRotateCounterclockwise; return button; }, []);
const handleLoaded = useCallback(() => { // Get the element of React PDF. const topBarRight = ref.current?.querySelector('[data-rp="topBarRight"]'); // Add the button element to the top bar. topBarRight?.prepend(buttonRotateClockwise); topBarRight?.prepend(buttonRotateCounterclockwise); }, [buttonRotateClockwise]);
// Remove the button element when the component is cleaned up. const cleanupOnLoaded = useCallback(() => { if (buttonRotateClockwise) { buttonRotateClockwise.remove(); } if (buttonRotateCounterclockwise) { buttonRotateCounterclockwise.remove(); } }, [buttonRotateClockwise, buttonRotateCounterclockwise]);
return ( <> <RPDefaultLayout ref={ref} style={{ width: "100%", height: "550px" }} onLoaded={handleLoaded} cleanupOnLoaded={cleanupOnLoaded} > <RPPages /> </RPDefaultLayout> </> );};
// App.tsx (Parent Component)import { RPConfig, RPProvider } from "@pdf-viewer/react";import { AppPdfViewer } from "./components/AppPdfViewer";export default function App{ return ( <RPConfig> <RPProvider src="https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf"> <AppPdfViewer /> </RPProvider> </RPConfig> )}
// AppPdfViewer.tsx (Child Component)import React, { type FC , useCallback, useMemo, useRef } from "react";
export const AppPdfViewer : FC = () => { const { rotate, setRotate } = useRotationContext(); const ref = useRef<HTMLDivElement>(null)
// Rotate 90 degrees in a clockwise direction const handleRotateClockwise = useCallback(() => { setRotate((prev) => (prev + 90) % 360); }, [setRotate] // Rotate 90 degrees in a counterclockwise direction const handleRotateCounterclockwise = useCallback(() => { setRotate((prev) => (prev - 90 + 360) % 360); }, [setRotate] // Create the button element. const buttonRotateClockwise = useMemo(() => { const button = document.createElement("button"); button.innerHTML = "↻"; button.onclick = handleRotateClockwise; return button; }, []);
const buttonRotateCounterclockwise = useMemo(() => { const button = document.createElement("button"); button.innerHTML = "↺"; button.onclick = handleRotateCounterclockwise; return button; }, []);
const handleLoaded = useCallback(() => { // Get the element of React PDF. const topBarRight = ref.current?.querySelector('[data-rp="topBarRight"]'); // Add the button element to the top bar. topBarRight?.prepend(buttonRotateClockwise); topBarRight?.prepend(buttonRotateCounterclockwise); }, [buttonRotateClockwise]);
// Remove the button element when the component is cleaned up. const cleanupOnLoaded = useCallback(() => { if (buttonRotateClockwise) { buttonRotateClockwise.remove(); } if (buttonRotateCounterclockwise) { buttonRotateCounterclockwise.remove(); } }, [buttonRotateClockwise, buttonRotateCounterclockwise]);
return ( <> <RPDefaultLayout ref={ref} style={{ width: "100%", height: "550px" }} onLoaded={handleLoaded} cleanupOnLoaded={cleanupOnLoaded} > <RPPages /> </RPDefaultLayout> </> );};
Adding Custom Buttons in a Parent Component
With the React Contenxt API, you can invoke key features like rotating pages, zooming, or navigating through the document without relying solely on the viewer’s built-in UI. This approach is particularly useful for users who need to customize specific designs or meet unique usability requirements.
Example: External Button to Trigger a Rotate Function
React PDF provides a useRotationContext through its React Context API, enabling you to control page rotation from outside the viewer. This flexibility allows you to integrate rotation controls into custom buttons or external components, making the viewer adaptable to your application’s needs.
Here are the available functions for managing page rotation using the Rotate Context.
import { useRotationContext, RPConfig, RPProvider, RPDefaultLayout, RPPages} from "@pdf-viewer/react";
const CustomRotationLayout = () => { // Access the rotation control from the context const { rotate, setRotate } = useRotationContext();
// Rotate 90 degrees in a clockwise direction const handleRotateClockwise = () => { setRotate((prev) => (prev + 90) % 360); };
// Rotate 90 degrees in a counterclockwise direction const handleRotateCounterclockwise = () => { setRotate((prev) => (prev - 90 + 360) % 360); };
return ( <div> {rotate} degrees <br /> <button onClick={handleRotateClockwise}>Rotate Clockwise</button> <br /> <button onClick={handleRotateCounterclockwise}> Rotate Counterclockwise </button> </div> );};
export const AppPdfViewer = () => { return ( <RPConfig> <RPProvider src="https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf"> <CustomRotationLayout/> <RPDefaultLayout style={{ width: "100%", height: "550px" }} > <RPPages /> </RPDefaultLayout> </RPProvider> </RPConfig> );};
import { useRotationContext, RPConfig, RPProvider, RPDefaultLayout, RPPages} from "@pdf-viewer/react";import { type FC } from "react";
const CustomRotationLayout: FC = () => { // Access the rotation control from the context const { rotate, setRotate } = useRotationContext();
// Rotate 90 degrees in a clockwise direction const handleRotateClockwise = () => { setRotate((prev) => (prev + 90) % 360); };
// Rotate 90 degrees in a counterclockwise direction const handleRotateCounterclockwise = () => { setRotate((prev) => (prev - 90 + 360) % 360); };
return ( <div> {rotate} degrees <br /> <button onClick={handleRotateClockwise}>Rotate Clockwise</button> <br /> <button onClick={handleRotateCounterclockwise}> Rotate Counterclockwise </button> </div> );};
export const AppPdfViewer : FC = () => { return ( <RPConfig> <RPProvider src="https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf"> <CustomRotationLayout/> <RPDefaultLayout style={{ width: "100%", height: "550px" }} > <RPPages /> </RPDefaultLayout> </RPProvider> </RPConfig> );};