Svelte
Svelte 5 components and hooks for Inkbar. The components are unstyled primitives built on @inkbar/core.
Install the packages as the Quickstart shows.
Svelte 5.46.4 or later is a peer dependency. Add the Vite plugin from @inkbar/core/vite so PDF.js can load its worker and fonts; Loading assets covers it. The package ships .svelte sources, so your bundler compiles them for your target, including the server.
<script lang="ts"> import { InkRoot, InkViewport, InkToolbar, InkToolButton, InkToolbarSeparator, InkUndo, InkRedo, InkPrevPage, InkPageInput, InkPageCount, InkNextPage, InkZoomSelect, InkNotePopover, InkSuggestionCard, } from '@inkbar/svelte' import type { Annotation } from '@inkbar/core' import { assets } from 'virtual:inkbar-assets' import '@inkbar/theme/theme.css'
let { signedUrl, documentId }: { signedUrl: string; documentId: string } = $props()
let annotations = $state.raw<Annotation[]>([])</script>
<InkRoot src={{ url: signedUrl, key: documentId }} {assets} bind:annotations> <InkToolbar> <InkToolButton tool="view" /> <InkToolButton tool="select" /> <InkToolButton tool="highlight" /> <InkToolButton tool="redact-area" /> <InkToolButton tool="note" /> <InkToolbarSeparator /> <InkUndo /> <InkRedo /> </InkToolbar> <InkViewport /> <InkToolbar label="Page and zoom"> <InkPrevPage /> <InkPageInput /> <InkPageCount /> <InkNextPage /> <InkZoomSelect /> </InkToolbar> <InkNotePopover /> <InkSuggestionCard /></InkRoot>Each control is a separate component, so put them in any order or leave some out. Buttons show icons from @inkbar/icons. Pass your own as children to replace one, and use InkIcon to put the same icons on your own buttons. Toolbars lists every control.
bind:annotations makes your list the source of truth. Declare it with $state.raw, since Inkbar replaces the list whole. Leave the binding off and Inkbar keeps the state itself, with undo and redo. Controlled mode covers the details.
tool, page and zoom are bindable too, and each has a callback prop such as onToolChange if you would rather not bind.
Note popover
Section titled “Note popover”InkNotePopover opens the thread of the activated note. A new note has no text yet, so the form first writes the note’s own text (labelled “Comment”); after that it adds replies.
A root note with no text and no replies is removed when its popover closes, or when activation moves to another annotation, so abandoned notes do not stay on the page. Locked notes are never removed. If you add notes yourself (add({ type: 'note', ... })), give them contents, or they will be removed the first time someone opens and closes them.
Custom rendering
Section titled “Custom rendering”Slots are snippets. InkViewport takes loading, empty, error, annotation and pageError. InkNotePopover and InkSuggestionCard take children as a snippet that receives the popover’s props. InkToolButton takes children as a snippet of { active, label }. The props each receives are typed as AnnotationSlotProps, NotePopoverSlotProps and SuggestionCardSlotProps, the same types the Vue and React packages export.
InkRoot, InkToolbar, InkToolButton, InkUndo, InkRedo, InkPrevPage, InkNextPage, InkZoomIn and InkZoomOut take a child snippet to render your own element. Spread the props it gives you on that element. The snippet type is exported as ChildSnippet.
<InkToolbar> {#snippet child({ props })} <nav {...props} class="my-toolbar">…</nav> {/snippet}</InkToolbar>Keyboard
Section titled “Keyboard”The scroll area takes focus. From there:
- With the
highlightorredact-areatool active,Enterturns the text selection into a mark. - With the
notetool active,Enterplaces a note at the centre of the current page and opens it.
On a focused annotation:
EnterorSpaceselects and activates it. A note opens its thread and a suggested redaction its card; other types are only selected.ArrowUpandArrowDownmove to the previous or next annotation on the page.Shift+ arrow moves it by 1 pt, or by 10 pt withAltas well.CtrlorCmd+Shift+ arrow resizes an area redaction, text box or shape.DeleteorBackspaceremoves it.
The full table, with the zoom and page keys, is in Annotations.
Styling
Section titled “Styling”Components carry state on data attributes, for example [data-annotation][data-state="selected"] and [data-tool="highlight"][data-state="on"]. @inkbar/theme gives a default look. Your own CSS overrides it without !important. InkRoot, InkViewport, InkToolbar and the toolbar controls accept class.
useDocument, useAnnotations, useTool, useSelection, useZoom, usePage, useHistory and useEmbedded read the state of the nearest InkRoot. Call them during component initialisation, as you would getContext. Build your own controls or side panel from them.
Each hook returns an object whose reactive members are getters. Read useZoom().zoom in your template or an effect and it tracks. Destructure it (const { zoom } = useZoom()) and you copy the value once, so keep the object and read through it.