Skip to content
Build Your Own Toolbar with MUI

This guide walks through building a custom PDF viewer toolbar with MUI (Material-UI). You use React PDF Kit for PDF rendering and MUI for toolbar UI such as icons, theming, or full custom layout. The library supports two ways to build with MUI:

  1. Build with MUI using the default toolbar - Use the built-in RPLayout and RPHorizontalBar with a slots map to show or hide default tools, and the icons prop to use MUI icons. Optionally wrap in RPTheme with customVariables to style the toolbar.
  2. Build with MUI using Context API - Build the entire toolbar from scratch with MUI only (Paper, Toolbar, Stack, etc.) and wire every action through the library’s React Context hooks (zoom, pagination, search, print, download). Full control over layout and styling.

The default toolbar layout is provided by the library. You can choose which tools to show or hide and style them with RPTheme and MUI icons.

Use RPLayout and RPHorizontalBar with a slots map to show or hide built-in tools, and the icons prop to swap in MUI icons.

Use this approach when you want the default toolbar layout and behavior with MUI icons and optional theming.

  1. Define an enum (or object) for the toolbar slot keys the viewer expects. This keeps slot names in one place and matches the keys used by RPHorizontalBar.

    src/lib/types.js
    export const PDF_VIEWER_TOOL = Object.freeze({
    searchTool: "searchTool",
    pageNavigationTool: "pageNavigationTool",
    zoomTool: "zoomTool",
    themeSwitcher: "themeSwitcher",
    downloadTool: "downloadTool",
    openFileTool: "openFileTool",
    printTool: "printTool",
    fullscreenTool: "fullscreenTool",
    jumpNavigationTool: "jumpNavigationTool",
    selectionModeTool: "selectionModeTool",
    rotateTool: "rotateTool",
    viewModeTool: "viewModeTool",
    scrollModeTool: "scrollModeTool",
    documentProperties: "documentProperties",
    thumbnailTool: "thumbnailTool",
    sidebarEnable: "sidebarEnable",
    dropFileZone: "dropFileZone",
    })
  2. Build a slots object that enables only the tools you want. For each slot key, set true to use the default tool or false to hide it. No custom components are required, the library’s default tools are used; you only control visibility.

    src/components/AppCustomizePdfViewer.jsx
    import { PDF_VIEWER_TOOL } from "../lib/types";
    const slots = Object.values(PDF_VIEWER_TOOL);
    const AVAILABLE_TOOLS = [
    PDF_VIEWER_TOOL.searchTool,
    PDF_VIEWER_TOOL.zoomTool,
    PDF_VIEWER_TOOL.downloadTool,
    PDF_VIEWER_TOOL.printTool,
    ];
    const renderSlots = Object.fromEntries(
    slots.map((key) => {
    return [key, AVAILABLE_TOOLS.includes(key)];
    }),
    );
    • AVAILABLE_TOOLS: Array of slot keys to show in the toolbar; all other slots are disabled.
    • renderSlots: Map of every slot key to true (show default tool) or false (hide).
  3. (Optional) Wrap the viewer in RPTheme and pass customVariables to style the default toolbar (tooltip, button padding, border radius, text color).

    <RPTheme
    customVariables={{
    "--rp-tooltip-background-color": "#0000008a",
    "--rp-button-padding": "5px",
    "--rp-button-border-radius": "20px",
    "--rp-text-color": "#0000008a",
    }}
    >
    {/* RPProvider and RPLayout go here */}
    </RPTheme>
  4. Use RPProvider with your PDF src, then RPLayout with toolbar.topbar.component set to RPHorizontalBar. Pass slots (from step 2) and icons to replace default icons with MUI icons from @mui/icons-material.

    src/components/AppCustomizePdfViewer.jsx
    import { Box } from "@mui/material";
    import {
    RPProvider,
    RPPages,
    RPLayout,
    RPHorizontalBar,
    RPTheme,
    } from "@react-pdf-kit/viewer";
    import { PDF_VIEWER_TOOL } from "../lib/types";
    import {
    DownloadOutlined,
    PrintOutlined,
    Search as SearchIcon,
    ZoomIn,
    ZoomOut,
    } from "@mui/icons-material";
    const slots = Object.values(PDF_VIEWER_TOOL);
    const AVAILABLE_TOOLS = [
    PDF_VIEWER_TOOL.searchTool,
    PDF_VIEWER_TOOL.zoomTool,
    PDF_VIEWER_TOOL.downloadTool,
    PDF_VIEWER_TOOL.printTool,
    ];
    const renderSlots = Object.fromEntries(
    slots.map((key) => {
    return [key, AVAILABLE_TOOLS.includes(key)];
    }),
    );
    const AppCustomizePdfViewer = () => {
    return (
    <RPTheme
    customVariables={{
    "--rp-tooltip-background-color": "#0000008a",
    "--rp-button-padding": "5px",
    "--rp-button-border-radius": "20px",
    "--rp-text-color": "#0000008a",
    }}
    >
    <RPProvider src="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf">
    <Box sx={{ height: "600px", width: "100%" }}>
    <RPLayout
    toolbar={{
    topbar: {
    component: (
    <RPHorizontalBar
    slots={renderSlots}
    icons={{
    searchIcon: <SearchIcon fontSize="small" />,
    zoomInIcon: <ZoomIn fontSize="small" />,
    zoomOutIcon: <ZoomOut fontSize="small" />,
    downloadIcon: <DownloadOutlined fontSize="small" />,
    printIcon: <PrintOutlined fontSize="small" />,
    }}
    />
    ),
    },
    }}
    >
    <RPPages />
    </RPLayout>
    </Box>
    </RPProvider>
    </RPTheme>
    );
    };
    export default AppCustomizePdfViewer;

An image of the default toolbar with MUI

Note

  • Use RPHorizontalBar to render the default horizontal toolbar. Control which tools are visible with slots and override the icons with MUI components via icons.
  • Use RPLayout to wrap the toolbar and RPPages.

Build the entire toolbar yourself with MUI only. Use Paper, Toolbar, Stack, IconButton, TextField, Tooltip, Typography and keep the toolbar inside RPProvider so it can use the Context Hooks. You control layout and styling completely.

Key pieces:

  • App - Wrap React PDF in RPConfig (with license key) and renders the viewer component (e.g. AppPdfViewer).
  • AppPdfViewer - Use RPProvider with a PDF src, a MUI Box, the MUI toolbar CustomHorizontalBar, and RPPages.
  • CustomHorizontalBar - Your MUI toolbar: Paper + Toolbar + Stack, composing Zoom, Pagination, Download, Print, and Search tools.
  • Tool components - Each tool uses one hook from @react-pdf-kit/viewer and renders only MUI controls.

The same Hooks power the MUI tool components in Build with MUI using the default toolbar (slots) and Build with MUI using Context API (custom bar).

MUI tool componentHookPurpose
ZoomTooluseZoomContextZoom in/out via setZoomLevel
PaginationToolusePaginationContextPage index, total, nextPage, prevPage
DownloadTooluseFileDownloadTrigger file download
PrintToolusePrintContextTrigger print
SearchTooluseSearchContextQuery, set query, prev/next match, counts
  1. At the root of your app, wrap the React PDF Viewer component in RPConfig and pass your domain token. Render AppPdfViewer inside it so the PDF viewer and its configuration are available to the rest of the tree.

    src/App.jsx
    import { RPConfig } from '@react-pdf-kit/viewer'
    import './App.css'
    import AppPdfViewer from './components/AppPdfViewer'
    function App() {
    return (
    <>
    <h1>Create your own toolbar with MUI</h1>
    <RPConfig licenseKey={"YOUR_DOMAIN_TOKEN"}>
    <AppPdfViewer />
    </RPConfig>
    </>
    )
    }
    export default App
  2. Create the AppPdfViewer component. Use RPProvider with your PDF URL and a MUI Box that contains the toolbar and RPPages. The toolbar must stay inside RPProvider so the context hooks (zoom, pagination, search, etc.) are available.

    src/components/AppPdfViewer.jsx
    import { Box } from '@mui/material'
    import { RPProvider, RPPages } from '@react-pdf-kit/viewer'
    import { CustomHorizontalBar } from './CustomHorizontalBar'
    const AppPdfViewer = () => {
    return (
    <RPProvider src="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf">
    <Box sx={{ height: '600px', width: '100%' }}>
    <CustomHorizontalBar />
    <RPPages />
    </Box>
    </RPProvider>
    )
    }
    export default AppPdfViewer
  3. Create the CustomHorizontalBar component using MUI’s Paper, Toolbar, and Stack. Put zoom, pagination, download, and print in one row, and place the search control in a second group aligned to the right so the layout stays clear.

    src/components/CustomHorizontalBar.jsx
    import { Paper, Stack, Toolbar } from '@mui/material'
    import { ZoomTool } from './ZoomTool'
    import { PaginationTool } from './PaginationTool'
    import { DownloadTool } from './DownloadTool'
    import { PrintTool } from './PrintTool'
    import { SearchTool } from './SearchTool'
    export const CustomHorizontalBar = () => {
    return (
    <Paper
    elevation={0}
    variant="outlined"
    square
    sx={{
    borderBottom: 'none',
    borderRadius: '8px 8px 0 0',
    bgcolor: 'action.hover',
    }}
    >
    <Toolbar variant="dense" disableGutters sx={{ gap: 1, px: 1.5, py: 0.75 }}>
    <Stack direction="row" alignItems="center" spacing={0.5} sx={{ border: 1, borderColor: 'divider', borderRadius: 1, px: 1, py: 0.5 }}>
    <ZoomTool />
    <PaginationTool />
    <DownloadTool />
    <PrintTool />
    </Stack>
    <Stack direction="row" alignItems="center" spacing={0.5} sx={{ flexGrow: 1, justifyContent: 'flex-end' }}>
    <SearchTool />
    </Stack>
    </Toolbar>
    </Paper>
    )
    }
  4. Each toolbar action is a small component that uses one hook from @react-pdf-kit/viewer for behavior and MUI components for the UI. Create the following files.

    4.1 ZoomTool: Use the useZoomContext hook to control zoom. Add zoom in and zoom out buttons that change the level by 10% each click, clamped between 10 and 1000.

    src/components/ZoomTool.jsx
    import { ZoomInOutlined, ZoomOutOutlined } from '@mui/icons-material'
    import { IconButton, Tooltip } from '@mui/material'
    import { useZoomContext } from '@react-pdf-kit/viewer'
    import { useCallback } from 'react'
    export const ZoomTool = () => {
    const { setZoomLevel } = useZoomContext()
    const handleZoomIn = useCallback(() => {
    setZoomLevel((prev) => Math.min(prev + 10, 1000))
    }, [setZoomLevel])
    const handleZoomOut = useCallback(() => {
    setZoomLevel((prev) => Math.max(prev - 10, 10))
    }, [setZoomLevel])
    return (
    <>
    <Tooltip title="Zoom out">
    <IconButton size="small" onClick={handleZoomOut} aria-label="Zoom out">
    <ZoomOutOutlined fontSize="small" />
    </IconButton>
    </Tooltip>
    <Tooltip title="Zoom in">
    <IconButton size="small" onClick={handleZoomIn} aria-label="Zoom in">
    <ZoomInOutlined fontSize="small" />
    </IconButton>
    </Tooltip>
    </>
    )
    }

    4.2 PaginationTool: Use the usePaginationContext hook to get the current page and total. Add previous and next buttons and a read-only display showing the current page and total (e.g. “3 / 12”).

    src/components/PaginationTool.jsx
    import { ArrowDownward, ArrowUpward } from '@mui/icons-material'
    import { IconButton, Stack, TextField, Tooltip, Typography } from '@mui/material'
    import { usePaginationContext } from '@react-pdf-kit/viewer'
    export const PaginationTool = () => {
    const { focusedPage, totalPages, nextPage, prevPage } = usePaginationContext()
    return (
    <Stack direction="row" alignItems="center" spacing={0}>
    <Tooltip title="Previous page">
    <IconButton size="small" onClick={prevPage} aria-label="Previous page">
    <ArrowUpward fontSize="small" />
    </IconButton>
    </Tooltip>
    <Tooltip title="Current page">
    <TextField
    value={focusedPage}
    size="small"
    variant="outlined"
    sx={{
    width: 48,
    '& .MuiInputBase-root': { bgcolor: 'background.paper' },
    '& .MuiOutlinedInput-input': { textAlign: 'center', py: 0.5 },
    }}
    slotProps={{ htmlInput: { readOnly: true } }}
    />
    </Tooltip>
    <Typography variant="body2" color="text.secondary" sx={{ px: 0.5, minWidth: 28 }}>
    / {totalPages}
    </Typography>
    <Tooltip title="Next page">
    <IconButton size="small" onClick={nextPage} aria-label="Next page">
    <ArrowDownward fontSize="small" />
    </IconButton>
    </Tooltip>
    </Stack>
    )
    }

    4.3 DownloadTool: Use the useFileDownload hook and call its download function when the user clicks. Add a download button so the user can save the PDF file.

    src/components/DownloadTool.jsx
    import { DownloadOutlined } from '@mui/icons-material'
    import { IconButton, Tooltip } from '@mui/material'
    import { useFileDownload } from '@react-pdf-kit/viewer'
    export const DownloadTool = () => {
    const { download } = useFileDownload()
    return (
    <Tooltip title="Download file">
    <IconButton size="small" onClick={download} aria-label="Download file">
    <DownloadOutlined fontSize="small" />
    </IconButton>
    </Tooltip>
    )
    }

    4.4 PrintTool: Use the usePrintContext hook and call its print function on click. Add a print button so the user can open the browser print dialog.

    src/components/PrintTool.jsx
    import { PrintOutlined } from '@mui/icons-material'
    import { IconButton, Tooltip } from '@mui/material'
    import { usePrintContext } from '@react-pdf-kit/viewer'
    export const PrintTool = () => {
    const { print } = usePrintContext()
    return (
    <Tooltip title="Print">
    <IconButton size="small" onClick={print} aria-label="Print">
    <PrintOutlined fontSize="small" />
    </IconButton>
    </Tooltip>
    )
    }

    4.5 SearchTool: Use the useSearchContext hook for the search query and match navigation. Add a text field for the search term, previous and next match buttons, and a small counter showing the current match and total (e.g. “2 / 5”).

    src/components/SearchTool.jsx
    import { ExpandLess, ExpandMore, Search as SearchIcon } from '@mui/icons-material'
    import { IconButton, InputAdornment, Stack, TextField, Tooltip, Typography } from '@mui/material'
    import { useSearchContext } from '@react-pdf-kit/viewer'
    export const SearchTool = () => {
    const { search, setSearch, prevMatch, nextMatch, totalMatches, currentMatchPosition } = useSearchContext()
    const handleSearch = (e) => {
    setSearch(e.target.value)
    }
    return (
    <Stack direction="row" alignItems="center" spacing={0.5}>
    <TextField
    value={search}
    onChange={handleSearch}
    size="small"
    placeholder="Search"
    variant="outlined"
    slotProps={{
    input: {
    startAdornment: (
    <InputAdornment position="start">
    <SearchIcon fontSize="small" color="action" />
    </InputAdornment>
    ),
    },
    htmlInput: { 'aria-label': 'Search' },
    }}
    sx={{
    width: 180,
    '& .MuiInputBase-root': { bgcolor: 'background.paper' },
    }}
    />
    <Tooltip title="Previous match">
    <IconButton size="small" onClick={prevMatch} aria-label="Previous match">
    <ExpandLess fontSize="small" />
    </IconButton>
    </Tooltip>
    <Tooltip title="Next match">
    <IconButton size="small" onClick={nextMatch} aria-label="Next match">
    <ExpandMore fontSize="small" />
    </IconButton>
    </Tooltip>
    <Typography variant="body2" color="text.secondary" sx={{ minWidth: 36 }}>
    {currentMatchPosition} / {totalMatches}
    </Typography>
    </Stack>
    )
    }
  5. (Optional) Add a small library (e.g. src/lib/const.ts or src/lib/const.js) for shared layout or style constants that you reuse across the toolbar or React PDF viewer components.

    src/lib/const.js
    export const CENTER_ALIGN_STYLE = { display: 'flex', alignItems: 'center' }

An image of create your own toolbar with MUI

Note

  • Use RPConfig at the app root and RPProvider with a PDF src.
  • Build the toolbar (CustomHorizontalBar) with MUI only Paper, Toolbar, Stack, IconButton, TextField, Tooltip, Typography, and @mui/icons-material and keep it inside RPProvider so it can use the context hooks.
  • Implement each action as a small MUI component with one hook: useZoomContext, usePaginationContext, useFileDownload, usePrintContext, useSearchContext.
  • Render RPPages in the same provider for the PDF. Compose toolbar and pages in one place (e.g. AppPdfViewer).