---
title: "Place PDF annotation at viewport center | Nutrient"
canonical_url: "https://www.nutrient.io/guides/web/knowledge-base/place-annotation-at-visible-center/"
md_url: "https://www.nutrient.io/guides/web/knowledge-base/place-annotation-at-visible-center.md"
last_updated: "2026-05-18T15:39:42.731Z"
description: "Learn how to create an annotation at the center of the visible viewport in Nutrient Web SDK, accounting for zoom, pan, and scroll state using coordinate transformation."
---

This guide explains how to create an annotation at the center of whatever part of a PDF page you’re currently viewing, i.e. the visible area after zooming or panning and not just the page’s geometric center.

This is common when, for example, you want to mark something exactly in the region you’re focused on, regardless of scroll or zoom state.

## How it works

The approach involves three main steps:

1. **Find the visible area** — The page and viewport are both represented by document object model (DOM) elements. Calculating the intersection lets you know exactly which part of the page is onscreen.

2. **Map to PDF coordinates** — The viewer works in both screen pixels (client coordinates) and PDF page coordinates (points). To place an annotation, convert the visible rectangle from pixel-based coordinates to PDF page coordinates.

3. **Calculate the center** — Once you have the visible rectangle on the page (in PDF points), compute the midpoint, which is where you’ll anchor your annotation.

## Implementation steps

To place an annotation at the visible center:

1. Identify both the page element for the current page and the viewport container in the DOM.

2. Use [`getBoundingClientRect`](https://www.nutrient.io/api/web/classes/NutrientViewer.TextSelection.html#getboundingclientrect) to get their positions in pixels.

3. Calculate their intersection to find the actual visible chunk of the page.

4. Convert the visible rectangle to PDF coordinates using [`transformContentClientToPageSpace`](https://www.nutrient.io/api/web/classes/NutrientViewer.Instance.html#transformcontentclienttopagespace).

5. Use these coordinates to place your annotation at the visual center.

## Code example

The function below demonstrates how to implement this logic:

```js

async function placeAnnotationAtVisibleCenter(instance) {
  const pageIndex = instance.viewState.currentPageIndex;

  // Get viewport and page bounding rectangles.
  const viewportRect = instance.contentDocument.querySelector(".PSPDFKit-Viewport").getBoundingClientRect();
  const pageRect = instance.contentDocument.querySelector(`[data-page-index="${pageIndex}"]`).getBoundingClientRect();

  // Adjust for toolbar overlap if present.
  const toolbar = instance.contentDocument.querySelector(".PSPDFKit-Toolbar");
  const toolbarHeight = toolbar? toolbar.getBoundingClientRect().height : 0;

  // Calculate visible intersection.
  const visibleLeft = Math.max(viewportRect.left, pageRect.left);
  const visibleTop = Math.max(viewportRect.top + toolbarHeight, pageRect.top);
  const visibleRight = Math.min(viewportRect.right, pageRect.right);
  const visibleBottom = Math.min(viewportRect.bottom, pageRect.bottom);

  // Check for zero-area intersection (page not visible).
  if (visibleLeft >= visibleRight || visibleTop >= visibleBottom) {
    throw new Error(
      "Page is not visible in viewport - cannot place annotation",
    );
  }

  // Calculate center of visible area.
  const centerX = (visibleLeft + visibleRight) / 2;
  const centerY = (visibleTop + visibleBottom) / 2;

  // Transform client coordinates to PDF page coordinates (handles zoom/pan).
  const pagePoint = instance.transformContentClientToPageSpace(
    new NutrientViewer.Geometry.Point({ x: centerX, y: centerY }),
    pageIndex,
  );

  // Create annotation at the visible center.
  return instance.create(
    new NutrientViewer.Annotations.TextAnnotation({
      pageIndex,
      boundingBox: new NutrientViewer.Geometry.Rect({
        left: pagePoint.x - 50,
        top: pagePoint.y - 25,
        width: 100,
        height: 50,
      }),
      text: {
        format: "plain",
        value: "Annotation at visible center",
      },
    }),
  );
}

```

[Try this example in the Playground](https://www.nutrient.io/demo/sandbox?p=eyJ2IjoxLCJjc3MiOiIvKiBBZGQgeW91ciBDU1MgaGVyZSAqL1xuXHQiLCJzZXR0aW5ncyI6eyJmaWxlTmFtZSI6ImJhc2ljLnBkZiJ9LCJqcyI6Ik51dHJpZW50Vmlld2VyLmxvYWQoe1xuICAuLi5iYXNlT3B0aW9ucyxcbiAgdGhlbWU6IE51dHJpZW50Vmlld2VyLlRoZW1lLkRBUkssXG59KS50aGVuKChpbnN0YW5jZSkgPT4ge1xuICBjb25zb2xlLmxvZygnTnV0cmllbnQgbG9hZGVkIScpO1xuICBjb25zb2xlLmxvZygnQVBJIGRvY3M6IGh0dHBzOi8vd3d3Lm51dHJpZW50LmlvL2FwaS93ZWIvJyk7XG4gIGNvbnNvbGUubG9nKCdHdWlkZXM6IGh0dHBzOi8vd3d3Lm51dHJpZW50LmlvL2d1aWRlcy93ZWIvJyk7XG4gIFxuICBhc3luYyBmdW5jdGlvbiBwbGFjZUFubm90YXRpb25BdFZpc2libGVDZW50ZXIoaW5zdGFuY2UpIHtcbiAgICBjb25zdCBwYWdlSW5kZXggPSBpbnN0YW5jZS52aWV3U3RhdGUuY3VycmVudFBhZ2VJbmRleDtcbiAgICBjb25zdCB2aWV3cG9ydEVsID0gaW5zdGFuY2UuY29udGVudERvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5QU1BERktpdC1WaWV3cG9ydCcpO1xuICAgIGNvbnN0IHZpZXdwb3J0UmVjdCA9IHZpZXdwb3J0RWwuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgY29uc3QgcGFnZUVsID0gaW5zdGFuY2UuY29udGVudERvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYFtkYXRhLXBhZ2UtaW5kZXg9XCIke3BhZ2VJbmRleH1cIl1gKTtcbiAgICBjb25zdCBwYWdlUmVjdCA9IHBhZ2VFbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICBjb25zdCB0b29sYmFyID0gaW5zdGFuY2UuY29udGVudERvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5QU1BERktpdC1Ub29sYmFyJyk7XG4gICAgY29uc3QgdG9vbGJhckhlaWdodCA9IHRvb2xiYXIgPyB0b29sYmFyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLmhlaWdodCA6IDA7XG4gICAgXG4gICAgY29uc3QgdmlzaWJsZUxlZnQgPSBNYXRoLm1heCh2aWV3cG9ydFJlY3QubGVmdCwgcGFnZVJlY3QubGVmdCk7XG4gICAgY29uc3QgdmlzaWJsZVRvcCA9IE1hdGgubWF4KHZpZXdwb3J0UmVjdC50b3AgKyB0b29sYmFySGVpZ2h0LCBwYWdlUmVjdC50b3ApO1xuICAgIGNvbnN0IHZpc2libGVSaWdodCA9IE1hdGgubWluKHZpZXdwb3J0UmVjdC5yaWdodCwgcGFnZVJlY3QucmlnaHQpO1xuICAgIGNvbnN0IHZpc2libGVCb3R0b20gPSBNYXRoLm1pbih2aWV3cG9ydFJlY3QuYm90dG9tLCBwYWdlUmVjdC5ib3R0b20pO1xuICAgIFxuICAgIGlmICh2aXNpYmxlTGVmdCA%2BPSB2aXNpYmxlUmlnaHQgfHwgdmlzaWJsZVRvcCA%2BPSB2aXNpYmxlQm90dG9tKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BhZ2UgaXMgbm90IHZpc2libGUgaW4gdmlld3BvcnQnKTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgY2VudGVyWCA9ICh2aXNpYmxlTGVmdCArIHZpc2libGVSaWdodCkgLyAyO1xuICAgIGNvbnN0IGNlbnRlclkgPSAodmlzaWJsZVRvcCArIHZpc2libGVCb3R0b20pIC8gMjtcbiAgICBcbiAgICBjb25zdCBwYWdlUG9pbnQgPSBpbnN0YW5jZS50cmFuc2Zvcm1Db250ZW50Q2xpZW50VG9QYWdlU3BhY2UoXG4gICAgICBuZXcgTnV0cmllbnRWaWV3ZXIuR2VvbWV0cnkuUG9pbnQoeyB4OiBjZW50ZXJYLCB5OiBjZW50ZXJZIH0pLFxuICAgICAgcGFnZUluZGV4XG4gICAgKTtcbiAgICBcbiAgICBjb25zdCBhbm5vdGF0aW9uID0gbmV3IE51dHJpZW50Vmlld2VyLkFubm90YXRpb25zLlRleHRBbm5vdGF0aW9uKHtcbiAgICAgIHBhZ2VJbmRleCxcbiAgICAgIGJvdW5kaW5nQm94OiBuZXcgTnV0cmllbnRWaWV3ZXIuR2VvbWV0cnkuUmVjdCh7XG4gICAgICAgIGxlZnQ6IHBhZ2VQb2ludC54IC0gNTAsXG4gICAgICAgIHRvcDogcGFnZVBvaW50LnkgLSAyNSxcbiAgICAgICAgd2lkdGg6IDEwMCxcbiAgICAgICAgaGVpZ2h0OiA1MFxuICAgICAgfSksXG4gICAgICB0ZXh0OiB7XG4gICAgICAgIGZvcm1hdDogJ3BsYWluJyxcbiAgICAgICAgdmFsdWU6ICdBbm5vdGF0aW9uIGF0IHZpc2libGUgY2VudGVyJ1xuICAgICAgfVxuICAgIH0pO1xuICAgIFxuICAgIGF3YWl0IGluc3RhbmNlLmNyZWF0ZShhbm5vdGF0aW9uKTtcbiAgICBjb25zb2xlLmxvZygn4pyTIEFubm90YXRpb24gcGxhY2VkIHN1Y2Nlc3NmdWxseSBhdCB2aXNpYmxlIGNlbnRlciEnKTtcbiAgfVxuXG4gIHBsYWNlQW5ub3RhdGlvbkF0VmlzaWJsZUNlbnRlcihpbnN0YW5jZSlcbiAgICAuY2F0Y2goZXJyID0%2BIGNvbnNvbGUuZXJyb3IoJ0Vycm9yOicsIGVyci5tZXNzYWdlKSk7XG59KTsifQ%3D%3D).

> `placeAnnotationAtVisibleCenter` isn’t a built-in API method. This implementation uses [`transformContentClientToPageSpace`](https://www.nutrient.io/api/web/classes/NutrientViewer.Instance.html#transformcontentclienttopagespace), which automatically handles zoom and pan transformations, and includes checks for toolbar overlap and zero-area intersections, as outlined in the common pitfalls section below.

## Why this approach is needed

Using this method enhances usability and ensures annotations are contextually relevant.

- **Page center isn’t enough** — Placing something at `page.width/2`, `page.height/2` always puts it at the geometric middle, which can be frustrating if you’re zoomed far away.

- **Screen coordinates aren’t sufficient** — Annotations require page-based coordinates to appear consistently across zooms and devices.

- **ViewState doesn’t provide visible bounds** — While ViewState tracks zoom and page indexes, it doesn’t include the visible rect/bounds; you need to compute it by calculating the intersection between the page and viewport elements.

## Common pitfalls and edge cases

When implementing this functionality, be aware of the following:

- **Toolbar overlap** — If the toolbar is inside the viewer, subtract its height when calculating the visible area.

- **Zero-area intersections** — If the visible rect is empty (for example, page not in viewport), don’t create an annotation.

- **DOM not loaded** — The code must run after page/viewport elements are present in the DOM.

## Further reading

For a deeper dive into mapping screen to page coordinates, refer to our [coordinate space](https://www.nutrient.io/guides/web/pspdfkit-for-web/coordinate-spaces.md) guide.
---

## Related pages

- [Listen to an annotation’s hover event](/guides/web/knowledge-base/annotations-hover-event.md)
- [How to add a custom toolbar item to display current zoom percentage](/guides/web/knowledge-base/add-custom-zoom-perentage.md)
- [Add Signature Initials](/guides/web/knowledge-base/add-signature-initials.md)
- [Add Listener Toolbar Item](/guides/web/knowledge-base/add-listener-toolbar-item.md)
- [Add Listener Text Note Annotation](/guides/web/knowledge-base/add-listener-text-note-annotation.md)
- [Add Custom Keyboard Shortcuts](/guides/web/knowledge-base/add-custom-keyboard-shortcuts.md)
- [Blurry Print Resolution](/guides/web/knowledge-base/blurry-print-resolution.md)
- [Create Highlight Annotations From Text Extraction Technology](/guides/web/knowledge-base/create-highlight-annotations-from-text-extraction-technology.md)
- [Automatic Annotation Field Tab Ordering](/guides/web/knowledge-base/automatic-annotation-field-tab-ordering.md)
- [Customize Page Indicator](/guides/web/knowledge-base/customize-page-indicator.md)
- [Check Document Contains Annotations](/guides/web/knowledge-base/check-document-contains-annotations.md)
- [Control Appearance Of Delete Button On Ink Annotations](/guides/web/knowledge-base/control-appearance-of-delete-button-on-ink-annotations.md)
- [Keep widget annotation dimensions consistent across devices](/guides/web/knowledge-base/consistent-widget-annotation-dimensions.md)
- [Change Default Line Width Ink Annotations](/guides/web/knowledge-base/change-default-line-width-ink-annotations.md)
- [Check Password Protected Files](/guides/web/knowledge-base/check-password-protected-files.md)
- [Delete All Annotations](/guides/web/knowledge-base/delete-all-annotations.md)
- [Default To Cloudy Border](/guides/web/knowledge-base/default-to-cloudy-border.md)
- [Deselect Text](/guides/web/knowledge-base/deselect-text.md)
- [Detect Pspdfkit Ui Loaded](/guides/web/knowledge-base/detect-pspdfkit-ui-loaded.md)
- [Determine Current Layout Mode](/guides/web/knowledge-base/determine-current-layout-mode.md)
- [Disable Resize Of Annotations](/guides/web/knowledge-base/disable-resize-of-annotations.md)
- [How to disable text annotation movement in web apps](/guides/web/knowledge-base/disable-text-annotation-movement.md)
- [Disable Context Menu](/guides/web/knowledge-base/disable-context-menu.md)
- [Download Exported Document](/guides/web/knowledge-base/download-exported-document.md)
- [Focus the delete button in a confirm dialog](/guides/web/knowledge-base/focus-delete-button-in-confirm-modal-component.md)
- [Extracting text and cursor position in annotations](/guides/web/knowledge-base/extract-annotation-text-and-retrieve-cursor-position.md)
- [Find Ink Annotation For Signature Form Field](/guides/web/knowledge-base/find-ink-annotation-for-signature-form-field.md)
- [Focus Viewer After Loading](/guides/web/knowledge-base/focus-viewer-after-loading.md)
- [Export Ink Annotation Image](/guides/web/knowledge-base/export-ink-annotation-image.md)
- [Get Entered Document Password](/guides/web/knowledge-base/get-entered-document-password.md)
- [Handling Clicks On Custom Overlays](/guides/web/knowledge-base/handling-clicks-on-custom-overlays.md)
- [Focus Widget Annotation](/guides/web/knowledge-base/focus-widget-annotation.md)
- [Get Visible Annotations](/guides/web/knowledge-base/get-visible-annotations.md)
- [How Do I Limit The Number Of Annotations](/guides/web/knowledge-base/how-do-i-limit-the-number-of-annotations.md)
- [Highlight required form fields](/guides/web/knowledge-base/highlight-required-fields.md)
- [Fix errors with unsupported form field actions](/guides/web/knowledge-base/handle-unsupported-form-field-actions.md)
- [How Do I Prevent Printing Annotations](/guides/web/knowledge-base/how-do-i-prevent-printing-annotations.md)
- [How Do I Disable Scrolling On Page Edges](/guides/web/knowledge-base/how-do-i-disable-scrolling-on-page-edges.md)
- [How Do I Toggle The Theme](/guides/web/knowledge-base/how-do-i-toggle-the-theme.md)
- [Image Attachments Lost Stamp Annotation Templates](/guides/web/knowledge-base/image-attachments-lost-stamp-annotation-templates.md)
- [How Do I Zoom To A Specific Value](/guides/web/knowledge-base/how-do-i-zoom-to-a-specific-value.md)
- [Resize multiline text fields to avoid overflow](/guides/web/knowledge-base/how-do-i-resize-form-fields.md)
- [License Registered Different Bundle Id](/guides/web/knowledge-base/license-registered-different-bundle-id.md)
- [How Do I Rotate A Page](/guides/web/knowledge-base/how-do-i-rotate-a-page.md)
- [How To Create Bookmarks From Outline Elements](/guides/web/knowledge-base/how-to-create-bookmarks-from-outline-elements.md)
- [Load Pdf From Stream](/guides/web/knowledge-base/load-pdf-from-stream.md)
- [Iterate over form fields and widgets](/guides/web/knowledge-base/iterate-over-form-fields.md)
- [Load Pdf Stub From String](/guides/web/knowledge-base/load-pdf-stub-from-string.md)
- [Load Pdf As Arraybuffer](/guides/web/knowledge-base/load-pdf-as-arraybuffer.md)
- [Nutrient Size Optimization](/guides/web/knowledge-base/nutrient-size-optimization.md)
- [Observe Document Editor Visibility](/guides/web/knowledge-base/observe-document-editor-visibility.md)
- [Link Text](/guides/web/knowledge-base/link-text.md)
- [Override Ink Signature Dialog](/guides/web/knowledge-base/override-ink-signature-dialog.md)
- [Loading Multiple Files](/guides/web/knowledge-base/loading-multiple-files.md)
- [Disabling automatic synchronization in Nutrient Web SDK](/guides/web/knowledge-base/manual-instant-sync.md)
- [Overview](/guides/web/knowledge-base/overview.md)
- [Prevent Editing Content Text Annotation](/guides/web/knowledge-base/prevent-editing-content-text-annotation.md)
- [Override User Agent](/guides/web/knowledge-base/override-user-agent.md)
- [Programmatic Comment Annotations](/guides/web/knowledge-base/programmatic-comment-annotations.md)
- [Persist Currently Edited Note Test](/guides/web/knowledge-base/persist-currently-edited-note-test.md)
- [Read-only forms](/guides/web/knowledge-base/read-only-forms.md)
- [Persist Ink Signatures Across Instances](/guides/web/knowledge-base/persist-ink-signatures-across-instances.md)
- [Prevent Shortcut Printing](/guides/web/knowledge-base/prevent-shortcut-printing.md)
- [Render Page Without Annotations](/guides/web/knowledge-base/render-page-without-annotations.md)
- [Render Night Mode](/guides/web/knowledge-base/render-night-mode.md)
- [Process Currently Rendered Pages](/guides/web/knowledge-base/process-currently-rendered-pages.md)
- [Render Document Full Height](/guides/web/knowledge-base/render-document-full-height.md)
- [Programmatically Navigate To Page](/guides/web/knowledge-base/programmatically-navigate-to-page.md)
- [Render Watermark When Printing](/guides/web/knowledge-base/render-watermark-when-printing.md)
- [Render Page Black White](/guides/web/knowledge-base/render-page-black-white.md)
- [Rotate Ink Annotation](/guides/web/knowledge-base/rotate-ink-annotation.md)
- [Restore Last Seen Page](/guides/web/knowledge-base/restore-last-seen-page.md)
- [Web Sdk Vs Dws Viewer](/guides/web/knowledge-base/web-sdk-vs-dws-viewer.md)
- [Show Annotations Properties As Tooltip](/guides/web/knowledge-base/show-annotations-properties-as-tooltip.md)
- [Easily zoom to specific annotations in PDF](/guides/web/knowledge-base/zoom-to-specific-annotation.md)
- [Render Visible Area In Current Page](/guides/web/knowledge-base/render-visible-area-in-current-page.md)
- [Show Focus Ring Read Only](/guides/web/knowledge-base/show-focus-ring-read-only.md)
- [Submit Ink Signatures With Form](/guides/web/knowledge-base/submit-ink-signatures-with-form.md)
- [Wait For Element Appear](/guides/web/knowledge-base/wait-for-element-appear.md)
- [Save Modified Pdf To Document Engine](/guides/web/knowledge-base/save-modified-pdf-to-document-engine.md)

