Skip to content
Set the Initial Page from the Last Visit as the Initial Page

When a PDF document is loaded, React PDF Kit can restore the page the user last visited after a reload or return visit:

  1. On first visit, the viewer opens at page 1.
  2. When the user navigates to another page, the current page index is saved in the browser (localStorage or a cookie).
  3. 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.

@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.

NameObjective
initialPagePage number shown on first render, using page 1 as the first page
onPageChangeFired when the user navigates, use it to persist the page

Persistence uses standard browser APIs. Pick one approach for your application.

NameObjective
localStorageStore the page index as a string keyed by the PDF source or document id
document.cookieStore the page index in a cookie when policy or server readable state is required

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;

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;
  • If the stored value is missing, not a number, or less than 1, both samples fall back to page 1.
  • initialPage is 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 lastPage key can restore the wrong page for a different PDF.

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.