MeldUI

Use Case: Toolbar Customization

Reorder groups, hide buttons, add a custom action.

The default toolbar covers most needs, but the toolbar prop lets you reorder groups, hide individual buttons, and add custom actions without forking the component.

Pattern

<script setup lang="ts">
import { DocumentViewer } from '@meldui/vue'
import type { ToolbarConfig } from '@meldui/vue'

const toolbar: ToolbarConfig = {
  groups: ['pageNav', 'zoom', 'panels'], // restrict + reorder
  hide: ['rotate-cw', 'rotate-ccw'], // hide individual buttons
  customButtons: [
    {
      id: 'share',
      label: 'Share',
      onClick: () => openShareDialog(),
    },
  ],
}
</script>

<template>
  <DocumentViewer
    source="/doc.pdf"
    wasm-url="/pdfium.wasm"
    :toolbar="toolbar"
    :features="{ zoom: true, outline: true, thumbnails: true }"
  />
</template>

groups

The default group order is:

;[
  'pageNav',
  'zoom',
  'rotate',
  'viewMode',
  'interactionMode',
  'search',
  'panels',
  'annotate',
  'stamp',
  'sign',
  'redact',
  'actions',
]

Pass a subset to hide the rest entirely. Pass a reordered array to change visual order:

// Annotation-first layout
const toolbar: ToolbarConfig = {
  groups: ['annotate', 'pageNav', 'zoom', 'panels', 'search', 'actions'],
}

hide

Hide individual buttons by id. The standard ids:

  • Page nav: prev-page, next-page
  • Zoom: zoom-out, zoom-preset, zoom-in
  • Rotate: rotate-ccw, rotate-cw
  • View / interaction: view-mode, interaction-mode
  • Search: search
  • Panels: thumbnails, outline, comments
  • Actions: download, print, fullscreen
const toolbar: ToolbarConfig = {
  hide: ['fullscreen', 'rotate-cw', 'rotate-ccw'],
}

customButtons

Each entry supports:

  • id — unique (required)
  • label — used as the button’s aria-label and tooltip (required)
  • onClick — handler (required)
  • isActive? — optional predicate that drives aria-pressed
  • isDisabled? — optional predicate that disables the button

Custom buttons render with a default icon and surface their label as a tooltip / aria-label; they are appended just before the actions group.

const toolbar: ToolbarConfig = {
  customButtons: [
    {
      id: 'approve',
      label: 'Approve',
      onClick: () => approveDocument(),
    },
    {
      id: 'request-changes',
      label: 'Request changes',
      onClick: () => openRequestChangesDialog(),
      isDisabled: () => !canRequestChanges.value,
    },
  ],
}

The CustomToolbarButton type also declares icon and group fields, but these are not applied by the current toolbar — custom buttons render with a default icon and are always appended near the actions group regardless of group. Don’t rely on icon/group until they’re wired up.

Hiding the toolbar entirely

For chrome-free embeds, pass an empty groups array:

const toolbar: ToolbarConfig = { groups: [] }

Users still get keyboard shortcuts (if features.keyboardShortcuts is on) and touch gestures (features.touchGestures).

Per-user toolbars

Conditionally configure based on the current user:

const toolbar = computed<ToolbarConfig>(() => ({
  groups: isReviewer.value
    ? ['pageNav', 'zoom', 'annotate', 'panels']
    : ['pageNav', 'zoom', 'actions'],
  hide: isReviewer.value ? [] : ['print', 'download'],
}))

For per-document access control, compute features (and downloadUrl) from the document’s backend permissions and remount with :key — see Share-link viewer.

See also