Message API/docs/components/files/page-selection

Page Selection

Navigate to a page from the host UI and listen for the corresponding selection result.

Overview

When a user clicks a page in your own page rail, send `selectPage` to the viewer. The payload can use either `pageIndex` or `pageNumber`, but page index is usually easier because it matches the `pageList` payload directly.

The viewer responds with `selectPageResult`, and the host should keep `pageList` as the source of truth for the final active-page state.

Quick Start

Send `selectPage` from the page click handler and listen for `selectPageResult` for confirmation.

Select by page index

viewerWindow.postMessage({
  type: 'selectPage',
  payload: { pageIndex: 2 }
}, '*');

Select by page number

viewerWindow.postMessage({
  type: 'selectPage',
  payload: { pageNumber: 3 }
}, '*');

Listen for selectPageResult

window.addEventListener('message', (event) => {
  if (event.data?.type !== 'selectPageResult') return;

  console.log(event.data.payload);
});

Complete Implementation

This example connects the page list UI to viewer navigation using page indexes.

function selectPage(pageIndex) {
  viewerWindow.postMessage({
    type: 'selectPage',
    payload: { pageIndex }
  }, '*');
}

window.addEventListener('message', (event) => {
  if (event.data?.type !== 'selectPageResult') return;
  console.log('Selection changed:', event.data.payload);
});
Complete Implementation

Complete HTML Example

Use this full standalone HTML document when you want a minimal host page for page selection.

html
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>PDF Page Selection Demo</title>
  <style>
    body {
      margin: 0;
      font-family: Arial, sans-serif;
      background: #0b1220;
      color: #e5e7eb;
    }
    .app {
      display: grid;
      grid-template-columns: 320px 1fr;
      gap: 12px;
      min-height: 100vh;
      padding: 12px;
    }
    .panel {
      border: 1px solid #2f3b4f;
      border-radius: 12px;
      background: #111827;
      padding: 12px;
      display: grid;
      gap: 10px;
      align-content: start;
    }
    .stack {
      display: grid;
      gap: 8px;
    }
    .row {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }
    input, button, select {
      width: 100%;
      border: 1px solid #2f3b4f;
      border-radius: 8px;
      background: #0f172a;
      color: #e5e7eb;
      padding: 8px 10px;
      font: inherit;
      box-sizing: border-box;
    }
    button {
      width: auto;
      cursor: pointer;
    }
    .pages {
      display: grid;
      gap: 8px;
      max-height: 60vh;
      overflow: auto;
    }
    .page {
      border: 1px solid #2f3b4f;
      border-radius: 10px;
      background: #0d1728;
      padding: 8px;
      display: grid;
      gap: 8px;
    }
    .page.active {
      border-color: #22d3ee;
    }
    iframe {
      width: 100%;
      height: calc(100vh - 24px);
      border: 1px solid #2f3b4f;
      border-radius: 12px;
      background: #04101c;
    }
    img {
      max-width: 100%;
      height: auto;
      display: block;
      background: #fff;
      border-radius: 8px;
    }
    .meta {
      font-size: 12px;
      color: #9ca3af;
    }
  </style>
</head>
<body>
  <div class="app">
    <div class="panel">
      <div class="stack">
      <input id="viewerUrl" value="http://localhost:5173" />
      <input id="fileUrl" placeholder="Public PDF URL" />
      <div class="row">
        <button id="loadViewerBtn">Load Viewer</button>
        <button id="loadPdfBtn">Load PDF</button>
      </div>
      <div id="pages" class="pages"></div>
    </div>
    </div>
    <iframe id="viewerFrame" title="PDF Page Selection Demo Viewer"></iframe>
  </div>
  <script>
const viewerFrame = document.getElementById('viewerFrame');
const viewerUrlInput = document.getElementById('viewerUrl');
const fileUrlInput = document.getElementById('fileUrl');
const pagesEl = document.getElementById('pages');
let latestPageList = null;

function post(type, payload) {
  viewerFrame.contentWindow.postMessage({ type, payload }, '*');
}

function loadViewer() {
  viewerFrame.src = viewerUrlInput.value.trim();
}

function loadPdf() {
  post('view', {
    fileUrl: fileUrlInput.value.trim(),
    displayName: 'Demo User',
    username: 'demo.user',
    email: 'demo@example.com'
  });
}

window.addEventListener('message', (event) => {
  if (event.source !== viewerFrame.contentWindow) return;
  const data = event.data;
  if (!data || typeof data !== 'object') return;
  if (data.type === 'pageList') {
    latestPageList = data.payload;
    renderPages(data.payload.pages);
  }
  onViewerMessage(data);
});

function renderPages(pages) {
  if (!pagesEl) return;
  pagesEl.innerHTML = '';
  pages.forEach((page) => {
    const item = document.createElement('div');
    item.className = page.isSelected ? 'page active' : 'page';
    const actions = pageActions(page);
    item.innerHTML = [
      page.thumbnailDataUrl
        ? '<img src="' + page.thumbnailDataUrl + '" width="' + (page.thumbnailWidth || 120) + '" height="' + (page.thumbnailHeight || 160) + '" />'
        : '<div class="meta">No thumbnail data</div>',
      '<div><strong>' + (page.label || ('Page ' + (page.index + 1))) + '</strong></div>',
      '<div class="meta">' + (page.title || 'Default') + '</div>',
      actions
    ].join('');
    wirePageActions(item, page);
    pagesEl.appendChild(item);
  });
}

document.getElementById('loadViewerBtn').addEventListener('click', loadViewer);
document.getElementById('loadPdfBtn').addEventListener('click', loadPdf);

function pageActions() { return '<button data-action="select">Select</button>'; }
function wirePageActions(item, page) {
  item.querySelector('[data-action="select"]').addEventListener('click', () => {
    post('selectPage', { pageIndex: page.index });
  });
}
function onViewerMessage(data) {
  if (data.type === 'selectPageResult') {
    console.log('selectPageResult', data.payload);
  }
}
  </script>
</body>
</html>
Complete HTML Example

Live Preview

Open the focused demo to click page cards and watch `selectPage` and `selectPageResult` live.

File Page Selection Demo

Focused live demo for page selection.

Preview opens in a large modal for zoom-friendly review.