Skip to content

Set the Page from the Last Visit as the Initial Page



Scenario

When a PDF document is loaded, Vue PDF Viewer 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 below use the same viewer integration and only the persistence layer differs.

What to Use

@vue-pdf-viewer/viewer provides declarative props and events for the initial page. You persist the page index in the browser and pass it back via initialPage on the next load.

NameObjective
initialPage1-based page number shown on first render
pageChangedFired when the user navigates, use it to persist the page

Browser APIs

Persistence uses standard browser APIs. Pick one approach per code example below.

NameObjective
localStorageStore the page index as a string keyed by pdfSrc
getCookie / setCookieStore the page index in document.cookie with a hashed name

Code examples

Local storage

Use this when you want the simplest client-side persistence in an SPA (Single Page App). The storage key includes the full PDF URL so each document keeps its own last page.

vue
<script setup lang="ts">
import { VPdfViewer } from '@vue-pdf-viewer/viewer'

const pdfSrc =
  'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'

// One key per document so different PDFs keep separate last-page values.
const storageKey = `current-page-${pdfSrc}`

function readLastPage(): number {
  const stored = localStorage.getItem(storageKey)
  if (!stored) return 1
  const page = parseInt(stored, 10)
  // VPdfViewer uses 1-based page numbers; ignore corrupt or sub-1 values.
  return Number.isFinite(page) && page >= 1 ? page : 1
}

// Read once before mount so :initial-page is set on first render.
const initialPage = readLastPage()

function handlePageChanged(page: number): void {
  localStorage.setItem(storageKey, String(page))
}
</script>

<template>
  <h1 :style="{ textAlign: 'center' }">
    Set the Page from the Last Visit as the Initial Page (Local Storage)
  </h1>
  <div :style="{ width: '100%', height: '700px' }">
    <VPdfViewer :src="pdfSrc" :initial-page="initialPage" @page-changed="handlePageChanged" />
  </div>
</template>

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 pdfSrc so URL characters are not part of the name.

vue
<script setup lang="ts">
import { VPdfViewer } from '@vue-pdf-viewer/viewer'

const pdfSrc =
  'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'

// Cookie names cannot contain URL characters (/, :, etc.), so hash the src instead.
function hashString(value: string): string {
  let hash = 0
  for (let i = 0; i < value.length; i++) {
    hash = (hash << 5) - hash + value.charCodeAt(i)
    hash |= 0 // Keep within 32-bit integer range.
  }
  return Math.abs(hash).toString(36)
}

const cookieName = `current-page-${hashString(pdfSrc)}`

function getCookie(name: string): string | null {
  // document.cookie is one string; escape the name so it is safe inside RegExp.
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  const match = document.cookie.match(new RegExp(`(?:^|; )${escaped}=([^;]*)`))
  const raw = match?.[1]
  return raw != null ? decodeURIComponent(raw) : null
}

function setCookie(name: string, value: string, maxAgeDays = 365): void {
  const maxAge = maxAgeDays * 24 * 60 * 60
  // path=/ makes the cookie visible on all routes; SameSite=Lax is a safe default for SPAs.
  document.cookie = `${name}=${encodeURIComponent(value)}; max-age=${maxAge}; path=/; SameSite=Lax`
}

function readLastPage(): number {
  const stored = getCookie(cookieName)
  if (!stored) return 1
  const page = parseInt(stored, 10)
  // VPdfViewer uses 1-based page numbers; ignore corrupt or sub-1 values.
  return Number.isFinite(page) && page >= 1 ? page : 1
}

// Read once before mount so :initial-page is set on first render.
const initialPage = readLastPage()

function handlePageChanged(page: number): void {
  setCookie(cookieName, String(page))
}
</script>

<template>
  <h1 :style="{ textAlign: 'center' }">
    Set the Page from the Last Visit as the Initial Page (Cookie)
  </h1>
  <div :style="{ width: '100%', height: '700px' }">
    <VPdfViewer :src="pdfSrc" :initial-page="initialPage" @page-changed="handlePageChanged" />
  </div>
</template>

Notes

  • If the stored value is missing, not a number, or less than 1, both samples fall back to page 1.
  • initialPage is read once before mount. Updating storage later in the same session does not change the viewer until the next full reload.
  • Clearing site data, cookies, or using private browsing may reset persistence.

Need More Control?

initialPage and pageChanged are enough to restore the last visit on reload.

Use the Page Controller instance API when you need imperative navigation. For example: Custom prev or next buttons, jumping to a page from search results, or syncing with an external UI.