Skip to content

React Quick Start

Build your first annotation viewer with Annota and React.

  • React 18+ or 19+ installed
  • Annota package installed (see Installation)
  • Basic knowledge of React hooks

Here’s the simplest possible annotation viewer:

import { AnnotaProvider, AnnotaViewer, Annotator } from "annota";
import { useState } from "react";
import "annota/dist/index.css";
function App() {
const [viewer, setViewer] = useState(null);
return (
<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={setViewer}
/>
<Annotator viewer={viewer} />
</div>
</AnnotaProvider>
);
}
export default App;

That’s it! You now have a working image viewer with annotation capabilities.

<AnnotaProvider slideId="my-slide">

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

<AnnotaViewer
options={{
tileSources: "/path/to/image.dzi",
prefixUrl: "...",
}}
onViewerReady={setViewer}
/>

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

<Annotator viewer={viewer} />

Creates the annotation overlay layer. Requires the viewer instance.

Let’s add tools for creating annotations:

import { AnnotaProvider, AnnotaViewer, Annotator, useTool } from "annota";
import { PointTool, RectangleTool, PolygonTool } from "annota";
import { useState } from "react";
function App() {
const [viewer, setViewer] = useState(null);
const [tool, setTool] = useState("point");
return (
<AnnotaProvider slideId="my-slide">
<div style={{ width: "100vw", height: "100vh" }}>
<Toolbar tool={tool} onToolChange={setTool} />
<AnnotaViewer
options={{
tileSources: "/path/to/image.dzi",
prefixUrl: "https://cdn.jsdelivr.net/npm/openseadragon@4/build/openseadragon/images/",
}}
onViewerReady={setViewer}
/>
<Annotator viewer={viewer}>
<ToolController viewer={viewer} tool={tool} />
</Annotator>
</div>
</AnnotaProvider>
);
}
function Toolbar({ tool, onToolChange }) {
return (
<div style={{ position: "absolute", top: 20, left: 20, zIndex: 10 }}>
<button onClick={() => onToolChange("point")}>Point</button>
<button onClick={() => onToolChange("rectangle")}>Rectangle</button>
<button onClick={() => onToolChange("polygon")}>Polygon</button>
<button onClick={() => onToolChange("pan")}>Pan</button>
</div>
);
}
function ToolController({ viewer, tool }) {
const tools = {
point: new PointTool(),
rectangle: new RectangleTool(),
polygon: new PolygonTool(),
};
useTool({
viewer,
handler: tool === "pan" ? null : tools[tool],
enabled: tool !== "pan",
});
return null;
}

Use React hooks to access annotations reactively:

import { useAnnotations, useSelection } from "annota";
function AnnotationList() {
const annotations = useAnnotations();
const selection = useSelection();
return (
<div style={{ position: "absolute", top: 20, right: 20, zIndex: 10 }}>
<h3>Annotations ({annotations.length})</h3>
<ul>
{annotations.map((ann) => (
<li key={ann.id}>
{ann.shape.type}
{selection.some((s) => s.id === ann.id) && " (selected)"}
</li>
))}
</ul>
</div>
);
}
import { useAnnotator } from "annota";
import { useEffect } from "react";
function LoadAnnotations() {
const annotator = useAnnotator();
useEffect(() => {
if (!annotator) return;
fetch("/api/annotations/slide-001")
.then((res) => res.json())
.then((annotations) => {
annotator.addAnnotations(annotations);
});
}, [annotator]);
return null;
}
import { useAnnotations } from "annota";
function SaveButton() {
const annotations = useAnnotations();
const handleSave = () => {
fetch("/api/annotations/slide-001", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(annotations),
});
};
return <button onClick={handleSave}>Save</button>;
}
<Annotator
viewer={viewer}
style={(annotation) => ({
fill: annotation.properties?.type === "tumor" ? "#FF0000" : "#00FF00",
fillOpacity: 0.3,
stroke: "#FFFFFF",
strokeWidth: 2,
})}
/>

Now that you have a working React annotation viewer:

Want to try Svelte instead? See the Svelte Quick Start