Use Case: Share-Link Viewer
Permission-gated read-only viewer for anonymous visitors (share-link / public-link patterns).
When a document is shared via a public link, you typically want the recipient to be able to read but not edit — no annotation, no thread replies, no download. You express this entirely through the features flags: a flag that’s off does not register its plugin and hides its toolbar button.
There is no separate permissions prop. Because DocumentViewer reloads a new document only on remount (it has no watch(source), and EmbedPDF freezes its plugin set at mount), per-document permissions map cleanly onto a per-document features object — bind :key to the document/share id so each document mounts with its own flags. See Per-document permissions for the mechanics.
Pattern
<script setup lang="ts">
import { computed } from 'vue'
import { DocumentViewer } from '@meldui/vue'
// Backend-supplied, per share link.
const share = props.share // { id, url, can_download, can_print, downloadUrl? }
const isAnonymousVisitor = !currentUser
const features = computed(() => ({
zoom: true,
search: true,
outline: true,
thumbnails: true,
// Capability + permission collapse into one flag. Anonymous visitors get a
// read-only viewer; the annotation/comment plugins aren't even registered.
annotations: !isAnonymousVisitor,
commentThreads: !isAnonymousVisitor,
download: share.can_download,
print: share.can_print,
}))
</script>
<template>
<DocumentViewer
:key="share.id"
:source="share.url"
:download-url="share.downloadUrl /* server-driven / watermarked copy */"
wasm-url="/pdfium.wasm"
:features="features"
/>
</template>
Why fold permissions into
features? The viewer remounts per document anyway, so each document already gets a fresh plugin registration. A restricted feature simply isn’t registered (smaller bundle, no dead button). If you instead want the capability to stay loaded but the button hidden — e.g. an admin who could download but you want the button gone in this view — usetoolbar.hideor acustomButtonsreplacement rather than turning off the feature.
Pre-seeded read-only review
A common share-link pattern: an editor publishes a reviewed document and shares a link. The recipient sees the highlights and threaded comments but can’t add their own. Keep annotations/commentThreads registered (so the seeded data renders) and gate authoring through the toolbar.
<script setup lang="ts">
import { onMounted, ref } from 'vue'
const viewer = ref()
onMounted(async () => {
const { annotations, threads } = await fetch(`/shared/${shareToken}`).then((r) => r.json())
await viewer.value?.loadAnnotations(annotations)
viewer.value?.loadThreads(threads)
})
</script>
<template>
<DocumentViewer
ref="viewer"
:key="shareToken"
:source="docUrl"
wasm-url="/pdfium.wasm"
:features="{ annotations: true, commentThreads: true, zoom: true }"
:toolbar="{ groups: ['pageNav', 'zoom', 'panels'] }"
/>
</template>
Restricting toolbar.groups to read/navigate groups (omitting annotate) means the seeded highlights/threads display in the side panel but the highlight/comment tools never appear — a read-only review view.
Embedding in another page
For embedding the viewer in a 3rd-party page (iframe, embedded widget), set a fixed size and register only what’s needed for viewing:
<DocumentViewer
:key="docId"
:source="docUrl"
wasm-url="/pdfium.wasm"
:features="{ zoom: true, search: true }"
class="h-[600px] w-full"
/>
Hiding the toolbar entirely
For a fully chrome-free embed, pass an empty groups array via toolbar:
const toolbar: ToolbarConfig = {
groups: [],
}
The viewport renders without any toolbar. Users still get keyboard shortcuts if features.keyboardShortcuts is on.
See also
- Per-document permissions (keyed remount)
- Features — flag table