Set the Initial Page from the Last Visit as the Initial Page
Scenario
Section titled “Scenario”When a PDF document is loaded, React PDF Kit can restore the page the user last visited after a reload or return visit:
- On first visit, the viewer opens at page 1.
- When the user navigates to another page, the current page index is saved in the browser (
localStorageor a cookie). - After the user reloads the app or comes back later, the viewer opens on that saved page.
This pattern is ideal for:
- Letting readers resume long documents where they left off
- Single page apps that reload without a server session
- Keeping separate last page values per PDF source URL
- Choosing local storage for simple SPA persistence, or cookies when site policy or server readable state is required
Both approaches use the same viewer integration. The difference is where you store the page index before passing it back to the viewer.
What to Use
Section titled “What to Use”@react-pdf-kit/viewer provides declarative props for the initial page and page change handling. You persist the page index in the browser and pass it back via initialPage on the next load.
| Name | Objective |
|---|---|
initialPage | Page number shown on first render, using page 1 as the first page |
onPageChange | Fired when the user navigates, use it to persist the page |
Browser APIs
Section titled “Browser APIs”Persistence uses standard browser APIs. Pick one approach for your application.
| Name | Objective |
|---|---|
localStorage | Store the page index as a string keyed by the PDF source or document id |
document.cookie | Store the page index in a cookie when policy or server readable state is required |
Code examples
Section titled “Code examples”Local storage
Section titled “Local storage”Use this when you want the simplest client side persistence in an SPA (Single Page App). The storage key includes the PDF source so each document keeps its own last page.
import { RPConfig, RPLayout, RPPages, RPProvider} from "@react-pdf-kit/viewer";import { useMemo } from "react";
const PDF_SOURCE = "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf";const DEFAULT_PAGE = 1;const LAST_VISIT_PAGE_KEY = `react-pdf-kit:last-visit-page:${PDF_SOURCE}`;
const normalizePage = (page) => { const parsedPage = typeof page === "number" ? page : Number.parseInt(String(page), 10);
if (!Number.isFinite(parsedPage) || parsedPage < DEFAULT_PAGE) { return DEFAULT_PAGE; }
return Math.floor(parsedPage);};
const readLastVisitedPage = () => { try { return normalizePage(window.localStorage.getItem(LAST_VISIT_PAGE_KEY)); } catch { return DEFAULT_PAGE; }};
const saveLastVisitedPage = (page) => { try { window.localStorage.setItem( LAST_VISIT_PAGE_KEY, String(normalizePage(page)) ); } catch { // Storage can be unavailable in private browsing or restricted contexts. }};
function App() { const restoredPage = useMemo(readLastVisitedPage, []);
return ( <> <h1 style={{ textAlign: "center" }}> Set the Page from the Last Visit as the Initial Page (Local Storage) </h1> <RPConfig> <RPProvider src={PDF_SOURCE} initialPage={restoredPage} onPageChange={saveLastVisitedPage} > <RPLayout toolbar> <RPPages /> </RPLayout> </RPProvider> </RPConfig> </> );}
export default App;import { RPConfig, RPLayout, RPPages, RPProvider} from "@react-pdf-kit/viewer";import { useMemo } from "react";
const PDF_SOURCE = "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf";const DEFAULT_PAGE = 1;const LAST_VISIT_PAGE_KEY = `react-pdf-kit:last-visit-page:${PDF_SOURCE}`;
const normalizePage = (page: unknown) => { const parsedPage = typeof page === "number" ? page : Number.parseInt(String(page), 10);
if (!Number.isFinite(parsedPage) || parsedPage < DEFAULT_PAGE) { return DEFAULT_PAGE; }
return Math.floor(parsedPage);};
const readLastVisitedPage = () => { try { return normalizePage(window.localStorage.getItem(LAST_VISIT_PAGE_KEY)); } catch { return DEFAULT_PAGE; }};
const saveLastVisitedPage = (page: number) => { try { window.localStorage.setItem( LAST_VISIT_PAGE_KEY, String(normalizePage(page)) ); } catch { // Storage can be unavailable in private browsing or restricted contexts. }};
function App() { const restoredPage = useMemo(readLastVisitedPage, []);
return ( <> <h1 style={{ textAlign: "center" }}> Set the Page from the Last Visit as the Initial Page (Local Storage) </h1> <RPConfig> <RPProvider src={PDF_SOURCE} initialPage={restoredPage} onPageChange={saveLastVisitedPage} > <RPLayout toolbar> <RPPages /> </RPLayout> </RPProvider> </RPConfig> </> );}
export default App;Cookie
Section titled “Cookie”Use this when cookies are preferred over localStorage, for example organizational policy, or when the page index should be available to server side code on the same site. The cookie name hashes the PDF source so URL characters are not part of the name.
import { RPConfig, RPLayout, RPPages, RPProvider} from "@react-pdf-kit/viewer";import { useMemo } from "react";
const PDF_SOURCE = "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf";const DEFAULT_PAGE = 1;
const normalizePage = (page) => { const parsedPage = typeof page === "number" ? page : Number.parseInt(String(page), 10);
if (!Number.isFinite(parsedPage) || parsedPage < DEFAULT_PAGE) { return DEFAULT_PAGE; }
return Math.floor(parsedPage);};
const hashString = (value) => { let hash = 0;
for (let index = 0; index < value.length; index += 1) { hash = (hash * 31 + value.charCodeAt(index)) % 1_000_000_007; }
return hash.toString(36);};
const LAST_VISIT_PAGE_COOKIE = `react-pdf-kit-last-visit-page-${hashString( PDF_SOURCE)}`;
const getCookie = (name) => { const cookie = document.cookie .split("; ") .find((item) => item.startsWith(`${name}=`));
if (!cookie) { return null; }
return decodeURIComponent(cookie.slice(name.length + 1));};
const setCookie = (name, value, maxAgeDays = 365) => { const maxAge = maxAgeDays * 24 * 60 * 60;
document.cookie = `${name}=${encodeURIComponent( value )}; max-age=${maxAge}; path=/; SameSite=Lax`;};
const readLastVisitedPage = () => { try { return normalizePage(getCookie(LAST_VISIT_PAGE_COOKIE)); } catch { return DEFAULT_PAGE; }};
const saveLastVisitedPage = (page) => { try { setCookie(LAST_VISIT_PAGE_COOKIE, String(normalizePage(page))); } catch { // Cookies can be unavailable in restricted browser contexts. }};
function App() { const restoredPage = useMemo(readLastVisitedPage, []);
return ( <> <h1 style={{ textAlign: "center" }}> Set the Page from the Last Visit as the Initial Page (Cookie) </h1> <RPConfig> <RPProvider src={PDF_SOURCE} initialPage={restoredPage} onPageChange={saveLastVisitedPage} > <RPLayout toolbar> <RPPages /> </RPLayout> </RPProvider> </RPConfig> </> );}
export default App;import { RPConfig, RPLayout, RPPages, RPProvider} from "@react-pdf-kit/viewer";import { useMemo } from "react";
const PDF_SOURCE = "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf";const DEFAULT_PAGE = 1;
const normalizePage = (page: unknown) => { const parsedPage = typeof page === "number" ? page : Number.parseInt(String(page), 10);
if (!Number.isFinite(parsedPage) || parsedPage < DEFAULT_PAGE) { return DEFAULT_PAGE; }
return Math.floor(parsedPage);};
const hashString = (value: string) => { let hash = 0;
for (let index = 0; index < value.length; index += 1) { hash = (hash * 31 + value.charCodeAt(index)) % 1_000_000_007; }
return hash.toString(36);};
const LAST_VISIT_PAGE_COOKIE = `react-pdf-kit-last-visit-page-${hashString( PDF_SOURCE)}`;
const getCookie = (name: string) => { const cookie = document.cookie .split("; ") .find((item) => item.startsWith(`${name}=`));
if (!cookie) { return null; }
return decodeURIComponent(cookie.slice(name.length + 1));};
const setCookie = (name: string, value: string, maxAgeDays = 365) => { const maxAge = maxAgeDays * 24 * 60 * 60;
document.cookie = `${name}=${encodeURIComponent( value )}; max-age=${maxAge}; path=/; SameSite=Lax`;};
const readLastVisitedPage = () => { try { return normalizePage(getCookie(LAST_VISIT_PAGE_COOKIE)); } catch { return DEFAULT_PAGE; }};
const saveLastVisitedPage = (page: number) => { try { setCookie(LAST_VISIT_PAGE_COOKIE, String(normalizePage(page))); } catch { // Cookies can be unavailable in restricted browser contexts. }};
function App() { const restoredPage = useMemo(readLastVisitedPage, []);
return ( <> <h1 style={{ textAlign: "center" }}> Set the Page from the Last Visit as the Initial Page (Cookie) </h1> <RPConfig> <RPProvider src={PDF_SOURCE} initialPage={restoredPage} onPageChange={saveLastVisitedPage} > <RPLayout toolbar> <RPPages /> </RPLayout> </RPProvider> </RPConfig> </> );}
export default App;- If the stored value is missing, not a number, or less than 1, both samples fall back to page 1.
initialPageis read once on initial render. Updating storage later in the same session does not change the viewer until the next full reload.- These examples normalize the saved value before render. They do not correct larger than document values after the PDF loads, because that would require imperative navigation.
- Clearing site data, cookies, or using private browsing may reset persistence.
- Scope saved page state by document identity. A single global
lastPagekey can restore the wrong page for a different PDF.
Need More Control?
Section titled “Need More Control?”initialPage and onPageChange are enough to restore the last visit on reload.
Use usePaginationContext when you need imperative navigation after the viewer is already running. For example, call goToPage to jump from a search result, custom table of contents, or external page input. Keep that separate from the last visit restore flow so normal page navigation does not keep returning to the saved page.