UI annotations in React.
Stet is a free, MIT-licensed UI annotation library. Add hand-sketched marks to live React interfaces while your existing controls keep their layout, events, and semantics.
Install and make your first mark
The package is @funsaized/stet. These ESM examples use the project’s generated framework templates. Pin your installed version and inspect its capabilities before using newer options.
React components attach to DOM refs after mount and render no wrapper element. Keep the ref on your existing button; conditionally mount Circle to toggle emphasis without removing the control.
"use client";
// Adapt these sample elements to existing source; retain their semantics and handlers.
import { useRef } from "react";
import { Circle } from "@funsaized/stet/react";
import "@funsaized/stet/style.css";
export function AnnotatedAction({ enabled = true }: { enabled?: boolean }) {
const target = useRef<HTMLButtonElement>(null);
return <>
<button ref={target} type="button">Review action</button>
{enabled && <Circle target={target} seed={42} description={"Review this action before continuing."} />}
</>;
}
Browse the canonical React templates
Keep marks in step with the interface
These lifecycle templates follow the repository checkout. Placement nudges such as labelOffsetY are unreleased; omit them with published 0.1.0 and check your installed capabilities before adapting the example.
For several marks or changing arrow destinations, useEffect can attach the core handles and return their cleanup function. Include enabled state and destination identity in the dependencies. Cleanup also makes development Strict Mode remounts safe.
"use client";
// Adapt to existing controls; annotation state never controls their presence.
import { useEffect, useRef } from "react";
import { arrow, circle, sticky, type StetHandle } from "@funsaized/stet";
function attachMarks(target: Element, destination: Element | null | undefined, enabled: boolean) {
const handles: StetHandle[] = [];
const destroy = () => { for (const handle of handles.splice(0).reverse()) handle.destroy(); };
try {
if (enabled) {
handles.push(circle(target, { seed: 42, description: "Review this action before continuing." }));
handles.push(sticky(target, { seed: 43, text: "Read the consequences before continuing." }));
if (destination) handles.push(arrow(target, destination, { seed: 44, label: "Consequences are explained here.", labelOffsetY: -12 }));
}
return destroy;
} catch (error) { destroy(); throw error; }
}
import "@funsaized/stet/style.css";
export function AnnotatedAction({ enabled = true, destination = 0 }: { enabled?: boolean; destination?: number }) {
const target = useRef<HTMLButtonElement>(null);
const to = useRef<HTMLParagraphElement>(null);
useEffect(() => {
if (target.current) return attachMarks(target.current, to.current, enabled);
}, [enabled, destination]);
return <>
<button ref={target} type="submit">Review action</button>
{destination > 0 && <p key={destination} ref={to}>Consequences of this action</p>}
</>;
}
Ink, layout, and accessible meaning
Import @funsaized/stet/style.css globally. Set --stet-stroke, --stet-fill, and --stet-paper to fit your design. SVG overlays do not occupy layout space or intercept pointer input.
Keep native labels, errors, keyboard focus, and event handlers on your controls. Give meaningful marks a description; color alone cannot explain a finding. Sticky notes and arrow labels supply readable text. Optional ink motion respects reduced-motion preferences.
Stet uses browser DOM APIs. Attach handles after mount, not during server rendering. Resize and scroll tracking cannot cover every transform or top-layer dialog; read placement limits and the full API reference.
Where this fits
Use a conditional Circle to identify a form finding, then remove the finding when your application resolves it.
Reproduce and annotate a form focus bug · Document an interactive activity inbox · Try all six primitives in the playground
Work with your coding agent
The agent inspects your installed package, validates an annotation plan, and adapts a React template. Plans are authoring artifacts, not a runtime selector engine. Browser verification must still check placement and behavior.
Install project Agent Skills · See the complete visual handoff workflow
Machine-readable capabilities · Annotation plan schema · Full API reference