Skip to content
Customize a Search Popover

An image of customize a search popover

Create a custom search functionality with a popover interface in React PDF Kit. The popover will include a search input field and additional options like “whole words” and “match case” filtering.

The useSearchContext hook provides access to the search functionality and configuration of the React PDF Viewer component. Use this hook to control search behavior and query updates.

Here are the key concepts we’re using for this example

NameObjective
setSearchFunction to update the search query
setSearchOptionsFunction to configure search behavior (wholeWords, matchCase)
currentMatchPositionTracks which match is currently highlighted
totalMatchesShows the total number of search results found
prevMatchFunction to go to the previous search result
nextMatchFunction to go to the next search result

To make the search popover experience smooth, you’ll want these 5 components:

Notes: This example will be using RPLayout with Individual Tools. Please refer to RPLayout for more information.

  1. Create a custom search icon component

    Icon.jsx
    export const SearchIcon = () => {
    return (
    <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 30 30">
    <path d="M 13 3 C 7.4889971 3 3 7.4889971 3 13 C 3 18.511003 7.4889971 23 13 23 C 15.396508 23 17.597385 22.148986 19.322266 20.736328 L 25.292969 26.707031 A 1.0001 1.0001 0 1 0 26.707031 25.292969 L 20.736328 19.322266 C 22.148986 17.597385 23 15.396508 23 13 C 23 7.4889971 18.511003 3 13 3 z M 13 5 C 17.430123 5 21 8.5698774 21 13 C 21 17.430123 17.430123 21 13 21 C 8.5698774 21 5 17.430123 5 13 C 5 8.5698774 8.5698774 5 13 5 z"></path>
    </svg>
    );
    }
    export const MoreOptionsIcon = () => {
    return (
    <>
    <svg
    fill="#000000"
    viewBox="0 0 32 32"
    id="Outlined"
    xmlns="http://www.w3.org/2000/svg"
    >
    <g id="SVGRepo_bgCarrier" stroke-width="0"></g>
    <g
    id="SVGRepo_tracerCarrier"
    stroke-linecap="round"
    stroke-linejoin="round"
    ></g>
    <g id="SVGRepo_iconCarrier">
    {" "}
    <title></title>{" "}
    <g id="Fill">
    {" "}
    <path d="M16,13a3,3,0,1,0,3,3A3,3,0,0,0,16,13Zm0,4a1,1,0,1,1,1-1A1,1,0,0,1,16,17Z"></path>{" "}
    <path d="M24,13a3,3,0,1,0,3,3A3,3,0,0,0,24,13Zm0,4a1,1,0,1,1,1-1A1,1,0,0,1,24,17Z"></path>{" "}
    <path d="M8,13a3,3,0,1,0,3,3A3,3,0,0,0,8,13Zm0,4a1,1,0,1,1,1-1A1,1,0,0,1,8,17Z"></path>{" "}
    </g>{" "}
    </g>
    </svg>
    </>
    );
    };
  2. Create a custom search popover component

    SearchPopover.jsx
    import { useSearchContext } from "@react-pdf-kit/viewer";
    import { useState } from "react";
    import { MoreOptionsIcon } from "./Icon";
    const SearchOptions = () => {
    const { setSearchOptions } = useSearchContext();
    return (
    <div
    style={{
    position: "absolute",
    minWidth: "max-content",
    zIndex: 5,
    top: "100%",
    right: 0,
    backgroundColor: "#f1f2f4",
    padding: "8px",
    borderRadius: "4px",
    }}
    >
    <div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
    <input
    type="checkbox"
    onClick={() =>
    setSearchOptions((val) => ({ ...val, wholeWords: !val.wholeWords }))
    }
    />
    <label>Whole words</label>
    </div>
    <div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
    <input
    type="checkbox"
    onClick={() =>
    setSearchOptions((val) => ({ ...val, matchCase: !val.matchCase }))
    }
    />
    <label>Matches case</label>
    </div>
    </div>
    );
    export const SearchPopover = () => {
    const {
    setSearch,
    currentMatchPosition,
    totalMatches,
    prevMatch,
    nextMatch,
    } = useSearchContext();
    const [isOpen, setIsOpen] = useState(false);
    return (
    <div
    style={{
    position: "absolute",
    minWidth: "max-content",
    zIndex: 5,
    top: "100%",
    right: 0,
    backgroundColor: "#f1f2f4",
    padding: "8px",
    display: "flex",
    borderRadius: "4px",
    alignItems: "center",
    gap: "4px",
    }}
    >
    <input
    placeholder="...Search"
    style={{ height: "100%" }}
    onChange={(e) => setSearch(e.target.value)}
    />
    <span>
    {currentMatchPosition} / {totalMatches}
    </span>
    <button onClick={prevMatch}>Prev</button>
    <button onClick={nextMatch}>Next</button>
    <button
    style={{
    position: "relative",
    width: "20px",
    height: "20px",
    padding: "4px",
    cursor: "pointer",
    borderRadius: "4px",
    border: 0,
    }}
    onClick={() => setIsOpen((prevValue) => !prevValue)}
    >
    <MoreOptionsIcon />
    </button>
    {isOpen && <SearchOptions />}
    </div>
    );
    };
  3. Create a custom search tool component from the custom search icon and custom search popover components

    CustomSearchTool.jsx
    import { useState } from "react";
    import { SearchIcon } from "./Icon";
    import { SearchPopover } from "./SearchPopover";
    export const CustomSearchTool = () => {
    const [isOpen, setIsOpen] = useState(false);
    return (
    <div
    style={{
    position: "relative",
    }}
    >
    <button
    style={{
    position: "relative",
    width: "28px",
    height: "28px",
    padding: "4px",
    cursor: "pointer",
    borderRadius: "4px",
    border: 0,
    }}
    onClick={() => setIsOpen((prevValue) => !prevValue)}
    >
    <SearchIcon />
    </button>
    {isOpen && <SearchPopover />}
    </div>
    );
    };
  4. Create a custom horizontal toolbar component

    CustomHorizontalTool.jsx
    import {
    FileDownloadTool,
    FileUploadTool,
    FullScreenTool,
    InputPageTool,
    NextPageTool,
    PreviousPageTool,
    PrintTool,
    ThemeSwitcherTool,
    ZoomInTool,
    ZoomLevelTool,
    ZoomOutTool,
    } from "@react-pdf-kit/viewer";
    import { CustomSearchTool } from "./CustomSearchTool";
    export const CustomHorizontalBar = () => {
    return (
    <>
    <PreviousPageTool />
    <InputPageTool />
    <NextPageTool />
    <div style={{ marginLeft: "auto" }}>
    <ZoomInTool />
    </div>
    <ZoomLevelTool />
    <ZoomOutTool />
    <div style={{ marginLeft: "auto" }} />
    <ThemeSwitcherTool />
    <FileUploadTool />
    <FileDownloadTool />
    <PrintTool />
    <FullScreenTool />
    <CustomSearchTool />
    </>
    );
    };
  5. Configure the custom horizontal toolbar component to the React PDF Viewer component

    CustomHorizontalToolbar.jsx
    import {
    RPLayout,
    RPPages,
    RPProvider,
    RPVerticalBar,
    RPConfig,
    } from "@react-pdf-kit/viewer";
    import { CustomHorizontalBar } from "./CustomHorizontalBar";
    const App = () => {
    return (
    <div>
    <RPConfig>
    <RPProvider src="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf">
    <div>
    <RPLayout
    toolbar={{
    topbar: { component: <CustomHorizontalBar /> },
    leftSidebar: { component: <RPVerticalBar /> },
    }}
    >
    <RPPages />
    </RPLayout>
    </div>
    </RPProvider>
    </RPConfig>
    </div>
    );
    };
    export default App

Notes

  • The CustomHorizontalBar component uses local state (isOpen) to control the visibility of the search popover
  • The SearchPopover component also maintains its own state for the options menu visibility
  • The zIndex: 5 ensures popovers appear above other content