MeldUI

Document Viewer: Customization

Customize the toolbar, side panels, and viewer chrome via ToolbarConfig, slots, and CSS class overrides.

The default DocumentViewer chrome is configured to work out of the box, but every part of the toolbar and side-panel layout is replaceable.

Toolbar customization

Pass a toolbar prop typed as ToolbarConfig:

import type { ToolbarConfig } from '@meldui/vue'

const toolbar: ToolbarConfig = {
  groups: ['nav', 'zoom', 'tools', 'panels', 'export'],
  hide: ['rotate', 'spread'],
  customButtons: [
    {
      id: 'share',
      label: 'Share',
      icon: 'IconShare',
      position: 'export',
      onClick: () => openShareDialog(),
    },
  ],
}
<DocumentViewer source="/doc.pdf" wasm-url="/pdfium.wasm" :toolbar="toolbar" ... />

groups

Reorders or restricts the toolbar groups. Default order is:

;['nav', 'zoom', 'view', 'tools', 'panels', 'search', 'export']

Pass a subset to hide other groups entirely. Pass a reordered array to change their visual order.

hide

Hides individual buttons by id. The default ids are: zoom-in, zoom-out, fit-page, fit-width, rotate-cw, rotate-ccw, spread, pan, fullscreen, print, download, highlight, comment, undo, redo, search-toggle, outline-toggle, thumbnails-toggle, annotations-toggle.

customButtons

Append your own buttons. Each requires:

  • id — unique within the toolbar
  • label — accessible label (visible in overflow menu)
  • icon — name of a @meldui/tabler-vue icon component
  • position — which group to attach to ('nav' | 'zoom' | 'view' | 'tools' | 'panels' | 'search' | 'export')
  • onClick — handler

Side panel customization

The three default panels are:

  • OutlineOutlinePanel.vue (renders document bookmarks as a tree)
  • ThumbnailsThumbnailsPanel.vue (lazy-loaded thumbnail grid)
  • AnnotationsAnnotationsPanel.vue (list of comments + replies)

Each is enabled by its corresponding features flag (outline, thumbnails, commentThreads) and toggled from the toolbar.

To replace a panel, import the host (ViewerSidePanel) and compose your own:

<script setup lang="ts">
import { ref } from 'vue'
import { DocumentViewer, ViewerSidePanel } from '@meldui/vue'
import MyCustomOutline from './MyCustomOutline.vue'

const sidePanelOpen = ref(false)
</script>

<template>
  <DocumentViewer source="/doc.pdf" wasm-url="/pdfium.wasm">
    <template #side-panel>
      <ViewerSidePanel v-if="sidePanelOpen" title="Custom Outline" @close="sidePanelOpen = false">
        <MyCustomOutline />
      </ViewerSidePanel>
    </template>
  </DocumentViewer>
</template>

ViewerSidePanel provides the host (header bar + close button + fixed width); your custom content fills its default slot.

Slot reference

SlotPurpose
default (no slot)None — the renderers fill the viewport
toolbar-extraAppend elements to the right side of the toolbar (before the overflow menu)
side-panelReplace the side-panel host. Bypasses the built-in outline / thumbnails / annotations panels
emptyOverride the empty state shown while the source is loading or null

Replacing format renderers

To use a custom PDF / image / text / markdown renderer, import the renderer components directly:

import { PdfViewer, ImageViewer, TextViewer, MarkdownViewer } from '@meldui/vue'

You can also import the underlying composables and build a fully custom layout:

import { useTouch, useCommands, useAnnotationThreads } from '@meldui/vue'

CSS class overrides

The viewer’s root element carries the document-viewer class and data-document-viewer attribute. Internal parts use prefixed classes (no document- prefix — see Theming):

ClassElement
document-viewerRoot container
viewer-toolbarSticky top toolbar
search-popoverSearch popover content
highlight-tooltipFloating tooltip on selected highlight
comment-formInline comment composer
pdf-viewerPDF renderer wrapper
pdf-pageEach page inside the PDF renderer
image-viewer / text-viewer / markdown-viewerNon-PDF renderer wrappers
annotations-panel / outline-panel / thumbnails-panelSide panel wrappers
annotation-rowA row inside the annotations panel
comment-markerOn-page sticky-note pin

Override these in your global CSS or via Tailwind utilities. The class prop on <DocumentViewer> is merged onto the root with cn() so per-instance overrides work cleanly.

<DocumentViewer class="rounded-xl border shadow-lg" source="/doc.pdf" wasm-url="/pdfium.wasm" />

Permissions

The permissions prop on ViewerToolbar (when composing your own layout) accepts a ViewerPermissions object that gates user-facing actions:

interface ViewerPermissions {
  canAnnotate?: boolean
  canResolveThreads?: boolean
  canDownload?: boolean
  canPrint?: boolean
  canSaveAsCopy?: boolean
}

This is separate from featuresfeatures decides whether a plugin is registered at all (bundle impact); permissions decides whether the (already-registered) UI is shown to the current user.

A read-only viewer for shared links:

<DocumentViewer
  :features="{ zoom: true, search: true, download: true, annotations: true, commentThreads: true }"
  :permissions="{ canAnnotate: false, canResolveThreads: false, canDownload: false }"
  ...
/>

See also