Quick Start
Build your first annotation viewer in minutes with Annota.
Minimal Example
Section titled “Minimal Example”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.
Understanding the Code
Section titled “Understanding the Code”Let’s break down what each component does:
1. AnnotaProvider
Section titled “1. AnnotaProvider”<AnnotaProvider slideId="my-slide">The AnnotaProvider wraps your application and provides annotation context. The slideId prop identifies this slide’s annotations (useful for multi-slide applications).
2. AnnotaViewer
Section titled “2. AnnotaViewer”<AnnotaViewer options={{ tileSources: "/path/to/image.dzi", prefixUrl: "...", }} onViewerReady={setViewer}/>The AnnotaViewer component creates and manages the OpenSeadragon viewer. It accepts:
options: OpenSeadragon configuration optionsonViewerReady: Callback when the viewer is initialized
3. Annotator
Section titled “3. Annotator”<Annotator viewer={viewer} />The Annotator component creates the annotation overlay layer. It must receive the OpenSeadragon viewer instance.
Adding Annotation Tools
Section titled “Adding Annotation Tools”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;}Now you have a complete annotation interface with multiple tools!
Accessing Annotations
Section titled “Accessing Annotations”Use hooks to access and display annotations:
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> );}Add this component inside AnnotaProvider:
<AnnotaProvider slideId="my-slide"> {/* ... other components ... */} <AnnotationList /></AnnotaProvider>Common Patterns
Section titled “Common Patterns”Loading Existing Annotations
Section titled “Loading Existing Annotations”import { useAnnotator } from "annota";import { useEffect } from "react";
function LoadAnnotations() { const annotator = useAnnotator();
useEffect(() => { if (!annotator) return;
// Load annotations from your API fetch("/api/annotations/slide-001") .then((res) => res.json()) .then((annotations) => { annotator.addAnnotations(annotations); }); }, [annotator]);
return null;}Saving Annotations
Section titled “Saving Annotations”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>;}Custom Styling
Section titled “Custom Styling”<Annotator viewer={viewer} style={(annotation) => ({ fill: annotation.properties?.type === "tumor" ? "#FF0000" : "#00FF00", fillOpacity: 0.3, stroke: "#FFFFFF", strokeWidth: 2, })}/>Troubleshooting
Section titled “Troubleshooting”Annotations not appearing
Section titled “Annotations not appearing”Make sure:
- The
Annotatorcomponent receives a validviewerprop - The
viewerprop is updated when OpenSeadragon initializes - You’re adding annotations after the annotator is ready
Tools not working
Section titled “Tools not working”Check that:
useToolhook receives a validviewerprop- The
enabledprop istrue - The tool handler is properly instantiated
TypeScript errors
Section titled “TypeScript errors”Ensure you have installed the type definitions:
npm install --save-dev @types/openseadragonNext Steps
Section titled “Next Steps”Now that you have a working annotation viewer:
- Core Concepts — Understand annotations, layers, and tools
- Annotation Tools — Learn about all available tools
- Layer System — Organize annotations into layers
- Events — Respond to annotation changes
- API Reference — Explore the complete API