Skip to content

Svelte Quick Start

Build your first annotation viewer with Annota and Svelte.

  • Svelte 5 installed
  • Annota package installed (see Installation)
  • Basic knowledge of Svelte 5 runes ($state, $derived, $effect)

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.

<AnnotaProvider slideId="my-slide">

Wraps your application and provides Svelte context for annotations. The slideId identifies this slide’s annotations.

<AnnotaViewer
options={{
tileSources: "/path/to/image.dzi",
prefixUrl: "...",
}}
onViewerReady={(v) => viewer = v}
/>

Creates the OpenSeadragon viewer instance. Returns the viewer via onViewerReady callback.

{#if viewer}
<Annotator {viewer} />
{/if}

Creates the annotation overlay layer. The #if block ensures it only renders when viewer exists.

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>

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>
<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>
<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>
<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} />
ReactSvelte
useState$state
useMemo$derived
useEffect$effect
useCallbackFunction declarations
useAnnotations()annotations()
useAnnotator()$derived(getAnnotator()())
useTool()tool()

Now that you have a working Svelte annotation viewer:

Want to try React instead? See the React Quick Start