Vue PDF Viewer didn't start as an annotation tool. It started as a viewer. Render a PDF, give it a toolbar, let people zoom and search and print. That's what people asked for first, and for its first year that was the whole product.
Then, our users wanted to mark up the documents they were reading. So we added the first annotation tools in September 2025: highlight, free text, and image. The requests didn't stop there, because a highlighter alone can't say everything people need to say on a document. A highlight means "pay attention here," an underline marks a reference, a strikethrough means "cut this." Force all three through one yellow highlight and you lose the distinction, so underline and strikethrough followed in February 2026. I worked on that plugin. That's the disclosure, and it's also why I can tell you where each option's seams are, ours included.
Here's the part worth knowing before you write any code. Vue PDF Viewer renders with PDF.js, like most of the Vue ecosystem, so PDF.js's own annotation editor was the obvious place to start on the creation side. It creates free text, ink, and stamps, and it has a highlight tool. It has no underline tool and no strikethrough tool. That gap has held for years. And the highlight it does give you is free-form rather than snapping cleanly to a text selection. So the text-selection toolkit ended up being something we built ourselves.
"Annotations" sounds like one feature. It's really three separate jobs. This walks through how to add PDF annotations in Vue.js across four approaches (PDF.js directly, vue-pdf-embed, @tato30/vue-pdf, and Vue PDF Viewer), plus a couple of escape hatches for when none of them fit.
Versions tested: Vue 3.5.x, pdfjs-dist 5.4.530, vue-pdf-embed 2.1.4, @tato30/vue-pdf 2.1.0, @vue-pdf-viewer/viewer 4.3.0 with @vue-pdf-viewer/annotation 1.6.0, on Chrome and macOS.
Rendering, creating, and persisting are three different jobs
There are three problems hiding inside the word "annotation," and conflating them is the most expensive mistake you can make. Someone drops a marked-up PDF into each demo, sees every highlight render perfectly, picks the smallest library, and finds out two sprints later that it can't create a highlight at all.
The first is rendering: a PDF may already contain annotations (highlights, notes, stamps someone added in Acrobat), and your viewer has to display them. This is the easy problem. Every library here handles it, because PDF.js handles it.
The second is creating: your user selects a sentence and highlights it, draws on the page, leaves a comment. Different problem. It needs editing tools, a toolbar, state, and a way to turn a drag of the mouse into an annotation the PDF format understands. This is where the libraries split apart, and where most of them stop.
The third is persisting: the highlight your user just made has to survive a reload, a round-trip to your backend, and ideally open correctly in someone else's PDF reader. Teams tend to find this one last, usually after shipping, when it's too late to change the first two choices.
A library can render annotations perfectly and still create none of its own, or let users create them and then save them where no other reader can read them. Decide which of the three you need before you evaluate anything:
| Approach | Render existing | Create new | Persist & export |
|---|---|---|---|
| PDF.js (direct) | Yes | Partial, no underline/strikethrough | DIY, and fragile |
| vue-pdf-embed | Yes | No | — |
| @tato30/vue-pdf | Yes | Yes (beta, single-page) | DIY |
| Vue PDF Viewer | Yes | Yes (5 types) | Save / print / download, XFDF in and out |
We'll take them one problem at a time, starting with the easy one.
Rendering existing PDF annotations costs you one prop
Someone marks up a contract in Acrobat. Three highlights, a strikethrough through a clause they want gone, an ink scribble in the margin, one sticky note. They email you the file and your Vue app has to show all of it.
That's one prop.
PDF.js ships an annotation layer that walks the annotation objects already stored in the file and draws them over the rendered page. Both lightweight Vue libraries expose it the same way, as a boolean.
vue-pdf-embed (hrynko's package on npm, not vue-pdf by FranckFreiburger, which is Vue 2 only and abandoned since 2021):
<script setup>
import VuePdfEmbed from 'vue-pdf-embed'
import 'vue-pdf-embed/dist/styles/annotationLayer.css'
</script>
<template>
<VuePdfEmbed source="/marked-up.pdf" annotation-layer />
</template>
@tato30/vue-pdf does the same job with a per-page API:
<script setup>
import { VuePDF, usePDF } from '@tato30/vue-pdf'
import '@tato30/vue-pdf/style.css'
const { pdf } = usePDF('/marked-up.pdf')
</script>
<template>
<VuePDF :pdf="pdf" annotation-layer />
</template>
If you forget the stylesheet, the layer mounts invisibly, which is the one catch here and costs an afternoon the first time. Otherwise you get the whole subtype set: link, text, highlight, underline, strikeout, squiggly, ink, stamp, free text. Underline and strikeout included, which matters shortly.
Our own changelog puts a date on just how far rendering ran ahead of creating. Vue PDF Viewer v1.5.0 shipped on 29 October 2024 and rendered highlight, strikeout, underline, squiggly, ink and free text. The first annotation a user of ours could create shipped in September 2025, eleven months later, and it was highlight, free text and image only. Underline and strikethrough creation didn't release until February 2026, sixteen months after we could already draw them on a page.
Creating PDF annotations is where the libraries come apart
Four ways to let a user make a mark, ordered by how much ships versus how much you build.
The PDF.js annotation editor
Everything below except pdf-lib is standing on this, so start here. PDF.js has an editor layer, the same one behind the markup tools in Firefox's built-in viewer. You turn it on and set a mode:
import { AnnotationEditorType } from 'pdfjs-dist'
// on a PDFViewer instance you've already set up yourself
pdfViewer.annotationEditorMode = { mode: AnnotationEditorType.HIGHLIGHT }
// FREETEXT | HIGHLIGHT | INK | STAMP, plus a signature editor in recent builds.
// There is no AnnotationEditorType.UNDERLINE.
// There is no AnnotationEditorType.STRIKEOUT.
That enum is the entire story. Free text, highlight, ink, stamp, and a signature editor built on top of stamp in recent builds. No underline tool. No strikethrough tool. Not on any version, including Mozilla's unreleased development code. This isn't a "not yet," it's the shape the project has had for years, and the annotations it refuses to author are the same ones it renders without complaint if they're already in the file.
On versions: if you're building straight on PDF.js, 6.1.200 is the current release, and none of the above changes. We test against the 5.x line ourselves, because Vue PDF Viewer's peer range (^5.4.530) excludes 6.x by construction.
The highlight tool you do get is free-form. There's a text-selection mode in there, and on a clean single-column page it behaves. Give it a two-column layout with a table in the middle, though, and the snapping gets too unreliable to put in front of users. That's our experience building on it rather than a documented limitation, so weigh it as such. What you can promise is "highlight a region," not "highlight that sentence."
Two more things fall on you. The marks the editor makes are HTML overlays on top of the page, not part of the PDF itself. Draw a highlight in and live demo are the fastest way to check whether it fits, including the parts it doesn't do.
SOCIAL SHARE CARD GENERATOR