Headless note annotations
A NoteAnnotation is the PDF specification’s sticky note — a small icon on the page that reveals a popup of text when clicked. The default note tool drops a note wherever the user clicks. Build your own note flow when you need a different placement strategy, a custom icon set, or notes that are created by something other than a click.
Looking for the visual side of this? See the annotations.note slot for replacing the popup that appears when a note annotation is clicked. This page covers the programmatic creation surface; the notes panel guide covers programmatic open/close of the popup itself.
Note annotations vs. annotation notes
These two concepts are easy to confuse:
- Note annotations (
NoteAnnotation) are sticky notes; they’re an annotation type with their own icon and position on the page. - Annotation notes are textual notes attached to other annotation types (rectangles, ellipses, ink, etc.). See the annotation notes guide for that case.
This page covers the first item: creating sticky note annotations from code.
When to use this
Reach for headless note annotations when your workflow involves:
- Bulk-importing comments from a server-side review system as sticky notes at known coordinates.
- A custom workflow that places a note in response to a search result, a backend event, or an analytical pass.
- A keyboard shortcut your application defines for “add a note here” with a specific icon.
- A multiuser review experience where notes appear at coordinates derived from a remote collaboration layer.
API reference
| Symbol | Notes |
|---|---|
NutrientViewer.Annotations.NoteAnnotation | The annotation class for sticky notes. |
NutrientViewer.NoteIcon | Enum of supported icon names, COMMENT, RIGHT_POINTER, RIGHT_ARROW, CHECK, CIRCLE, CROSS, INSERT, NEW_PARAGRAPH, NOTE, PARAGRAPH, HELP, STAR, KEY. |
The text property on NoteAnnotation holds the popup text content. It’s required and must be passed as { format: 'plain', value: string }; there’s no plain-string shorthand.
Example: Place a note at a click position
Wire a custom toolbar button to enter “place note” mode and drop a note at the user’s next click on the page:
const instance = await NutrientViewer.load({ container: "#viewer", document: "contract.pdf"});
const placeNoteButton = document.getElementById("place-note");
placeNoteButton.onclick = () => { enterPlaceNoteMode();};
function enterPlaceNoteMode() { const container = document.getElementById("viewer"); container.style.cursor = "crosshair";
container.addEventListener( "click", async (event) => { const rect = container.getBoundingClientRect(); const note = new NutrientViewer.Annotations.NoteAnnotation({ pageIndex: instance.viewState.currentPageIndex, text: { format: "plain", value: "New note" }, icon: NutrientViewer.NoteIcon.COMMENT, boundingBox: new NutrientViewer.Geometry.Rect({ left: event.clientX - rect.left, top: event.clientY - rect.top, width: 24, height: 24 }) });
await instance.create(note); container.style.cursor = ""; }, { once: true } );}For a production placement workflow, you’ll convert client coordinates to PDF page coordinates instead of using the raw client coordinates.
Example: Place a note with a specific icon
The icon property accepts any value from NutrientViewer.NoteIcon. Pick the icon that matches the intent of the note:
const note = new NutrientViewer.Annotations.NoteAnnotation({ pageIndex: 0, text: { format: "plain", value: "Help text, read this before signing." }, icon: NutrientViewer.NoteIcon.HELP, color: NutrientViewer.Color.fromHex("#FFC107"), boundingBox: new NutrientViewer.Geometry.Rect({ left: 100, top: 100, width: 24, height: 24 })});
await instance.create(note);Example: Bulk-create notes from server data
When the notes come from a backend review system, build the annotations from the payload and create them in a single call:
async function importReviewNotes(reviewData) { const notes = reviewData.map((entry) => new NutrientViewer.Annotations.NoteAnnotation({ pageIndex: entry.page, text: { format: "plain", value: entry.comment }, icon: NutrientViewer.NoteIcon.COMMENT, creatorName: entry.author, boundingBox: new NutrientViewer.Geometry.Rect({ left: entry.x, top: entry.y, width: 24, height: 24 }) }) );
await instance.create(notes);}Related
- Notes panel — Programmatically opening and closing the notes panel
- Annotation notes — The notes attached to other annotation types (different concept)
- Comments and replies — Nutrient’s collaborative comment system, which is different from sticky notes