0

PDF Form Fill Toggle

Switch a document between filling out its AcroForm fields and repositioning them.

"use client";

import { createPluginRegistration } from "@embedpdf/core";

About

<PdfFormFillToggle /> switches a document between two modes for its interactive form fields (AcroForm widgets — text boxes, checkboxes, radios, dropdowns):

  • Fill mode (pressed) — the form is fillable. Clicking a field puts the cursor in it and typing lands in the input.
  • Design mode (unpressed) — the same widgets behave as ordinary annotations you can select, move, and resize.

The form plugin doesn't render a layer of its own. Its field widgets render through the annotation plugin's <PdfAnnotationLayer>, and whether they fill or behave as annotations comes down to one thing: the annotation plugin's lock mode. Locking the form category is what lets a click fall through the widget to the field underneath. This toggle is a styled wrapper over exactly that — the annotation capability's setLocked:

provides.setLocked(
  fillMode
    ? { type: LockModeType.None }
    : { type: LockModeType.Include, categories: ["form"] },
);

Because it drives the annotation plugin, both plugins have to be registered and an <PdfAnnotationLayer> rendered — the toggle is one control in that setup, not a standalone widget.

Installation

pnpm dlx shadcn@latest add pdfcn/form-fill-toggle

Usage

Register the annotation and form plugins.

import { createPluginRegistration } from "@embedpdf/core";
import {
  AnnotationPluginPackage,
  LockModeType,
} from "@embedpdf/plugin-annotation/react";
import { FormPluginPackage } from "@embedpdf/plugin-form/react";
 
// Outside the component — a new array identity on every render tears the
// engine down and rebuilds it, losing scroll and zoom state. Starting the
// annotation plugin locked to the `form` category opens the document already
// fillable rather than in design mode.
const plugins = [
  createPluginRegistration(AnnotationPluginPackage, {
    locked: { type: LockModeType.Include, categories: ["form"] },
  }),
  createPluginRegistration(FormPluginPackage),
];

Render the annotation layer — the form fields render through it — and drop the toggle in the toolbar.

<PdfViewer
  documents={[{ url: "/form.pdf" }]}
  plugins={plugins}
  className="h-[720px]"
>
  <PdfToolbar>
    <PdfFormFillToggle />
  </PdfToolbar>
  <PdfViewerContent
    pageLayers={({ documentId, pageIndex }) => (
      <PdfAnnotationLayer documentId={documentId} pageIndex={pageIndex} />
    )}
  />
</PdfViewer>

Examples

Default

The document opens in fill mode. Click a field and type; toggle off to switch to design mode, where the widgets become selectable annotations.

"use client";

import { createPluginRegistration } from "@embedpdf/core";

Accessibility

The control is a single toggle button labelled "Fill form", with a tooltip to match. Its pressed state reflects whether the document is in fill mode, so a screen reader announces the mode rather than leaving it to the icon.

Filling and moving fields both require a pointer, since a field is selected by clicking it on the page — there's no keyboard equivalent for placing the cursor in a specific widget.

API Reference

PdfFormFillToggle

A <Toggle /> carrying data-slot="pdf-form-fill-toggle". Accepts every prop the underlying toggle takes; pressed, onPressedChange, aria-label, and the default icon are managed internally but can be overridden.

The toggle reads and writes the annotation plugin's lock mode through useAnnotation(documentId). Pressed sets { type: LockModeType.Include, categories: ["form"] } (fill mode); unpressed sets { type: LockModeType.None } (design mode). It renders disabled whenever there's no active document or the annotation plugin isn't registered.