Svelte Quick Start
Build your first annotation viewer with Annota and Svelte.
Prerequisites
Section titled “Prerequisites”- Svelte 5 installed
- Annota package installed (see Installation)
- Basic knowledge of Svelte 5 runes ($state, $derived, $effect)
Minimal Example
Section titled “Minimal Example”Here’s the simplest possible annotation viewer:
<script> import { AnnotaProvider, AnnotaViewer, Annotator } from "annota/svelte"; import "annota/dist/index.css";
let viewer = $state(null);</script>
<AnnotaProvider slideId="my-slide"> <div style="width: 100vw; height: 100vh;"> <AnnotaViewer options={{ tileSources: "/path/to/image.dzi", prefixUrl: "https://cdn.jsdelivr.net/npm/openseadragon@4/build/openseadragon/images/", }} onViewerReady={(v) => viewer = v} /> {#if viewer} <Annotator {viewer} /> {/if} </div></AnnotaProvider>That’s it! You now have a working image viewer with annotation capabilities.
Understanding the Code
Section titled “Understanding the Code”1. AnnotaProvider
Section titled “1. AnnotaProvider”<AnnotaProvider slideId="my-slide">Wraps your application and provides Svelte context for annotations. The slideId identifies this slide’s annotations.
2. AnnotaViewer
Section titled “2. AnnotaViewer”<AnnotaViewer options={{ tileSources: "/path/to/image.dzi", prefixUrl: "...", }} onViewerReady={(v) => viewer = v}/>Creates the OpenSeadragon viewer instance. Returns the viewer via onViewerReady callback.
3. Annotator
Section titled “3. Annotator”{#if viewer} <Annotator {viewer} />{/if}Creates the annotation overlay layer. The #if block ensures it only renders when viewer exists.
Adding Annotation Tools
Section titled “Adding Annotation Tools”Let’s add tools for creating annotations:
<script> import { AnnotaProvider, AnnotaViewer, Annotator, tool } from "annota/svelte"; import { PointTool, RectangleTool, PolygonTool } from "annota"; import "annota/dist/index.css";
let viewer = $state(null); let activeTool = $state("point");
const pointTool = $derived(new PointTool()); const rectangleTool = $derived(new RectangleTool()); const polygonTool = $derived(new PolygonTool());</script>
<AnnotaProvider slideId="my-slide"> <div style="width: 100vw; height: 100vh;"> <!-- Toolbar --> <div style="position: absolute; top: 20px; left: 20px; z-index: 10;"> <button onclick={() => activeTool = "point"}>Point</button> <button onclick={() => activeTool = "rectangle"}>Rectangle</button> <button onclick={() => activeTool = "polygon"}>Polygon</button> <button onclick={() => activeTool = "pan"}>Pan</button> </div>
<!-- Viewer --> <AnnotaViewer options={{ tileSources: "/path/to/image.dzi", prefixUrl: "https://cdn.jsdelivr.net/npm/openseadragon@4/build/openseadragon/images/", }} onViewerReady={(v) => viewer = v} />
<!-- Annotator and Tools --> {#if viewer} <Annotator {viewer}> <svelte:component this={tool} /> </Annotator> {/if} </div></AnnotaProvider>
<script> // Tool activation $effect(() => { if (!viewer) return;
const tools = { point: pointTool, rectangle: rectangleTool, polygon: polygonTool, };
tool({ viewer: () => viewer, handler: () => activeTool === "pan" ? null : tools[activeTool], enabled: () => activeTool !== "pan", }); });</script>Accessing Annotations with Stores
Section titled “Accessing Annotations with Stores”Use Svelte stores to access annotations reactively:
<script> import { annotations, selection } from "annota/svelte";
const getAnnotations = annotations(); const getSelection = selection();
// Use $derived to make them reactive const allAnnotations = $derived(getAnnotations()); const selectedAnnotations = $derived(getSelection());</script>
<div style="position: absolute; top: 20px; right: 20px; z-index: 10;"> <h3>Annotations ({allAnnotations.length})</h3> <ul> {#each allAnnotations as ann (ann.id)} <li> {ann.shape.type} {#if selectedAnnotations.some(s => s.id === ann.id)} (selected) {/if} </li> {/each} </ul></div>Common Svelte Patterns
Section titled “Common Svelte Patterns”Loading Existing Annotations
Section titled “Loading Existing Annotations”<script> import { onMount } from "svelte"; import { getAnnotator } from "annota/svelte";
const getAnnotatorFn = getAnnotator(); const annotator = $derived(getAnnotatorFn());
onMount(() => { if (!$annotator) return;
fetch("/api/annotations/slide-001") .then((res) => res.json()) .then((anns) => { $annotator.addAnnotations(anns); }); });</script>Saving Annotations
Section titled “Saving Annotations”<script> import { annotations } from "annota/svelte";
const getAnnotations = annotations(); const allAnnotations = $derived(getAnnotations());
function handleSave() { fetch("/api/annotations/slide-001", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(allAnnotations), }); }</script>
<button onclick={handleSave}>Save</button>Custom Styling
Section titled “Custom Styling”<script> const categoryStyle = (annotation) => { if (annotation.properties?.type === "tumor") { return { fill: "#FF0000", fillOpacity: 0.3, stroke: "#FFFFFF", strokeWidth: 2, }; } return { fill: "#00FF00", fillOpacity: 0.3, stroke: "#FFFFFF", strokeWidth: 2, }; };</script>
<Annotator {viewer} style={categoryStyle} />Svelte 5 Runes vs React Hooks
Section titled “Svelte 5 Runes vs React Hooks”| React | Svelte |
|---|---|
useState | $state |
useMemo | $derived |
useEffect | $effect |
useCallback | Function declarations |
useAnnotations() | annotations() |
useAnnotator() | $derived(getAnnotator()()) |
useTool() | tool() |
Next Steps
Section titled “Next Steps”Now that you have a working Svelte annotation viewer:
- Core Concepts — Understand annotations, layers, and tools
- Svelte Stores API — Learn about all Svelte stores
- Annotation Tools — Learn about all available tools
- Layer System — Organize annotations into layers
- Events — Respond to annotation changes
Want to try React instead? See the React Quick Start