ImageAnnotator
v1.0.3Updated 19 Oct 2026Mark up an image — arrow, box, freehand and text over a screenshot — with select / move / recolour / delete of existing marks, undo and redo, and a flattened PNG export at the image's natural size (fb_f8a55acb). Marks are DATA (AnnotationShape[] in natural pixels), not baked pixels, so they can be stored, re-opened and edited later; mark sizes are scaled to the capture's resolution, so a HiDPI screenshot shown small still exports legibly.
Preview
Live · 10 stories▸Why this is recommendedannotateannotationmarkupscreenshot+7
The default, out-of-the-box ImageAnnotator.
- ✓Mark up a screenshot before filing a bug report
- ✓Let support staff draw on the customer's capture when they answer
- ✓Circle the problem area on a photo or scan and attach the flattened image
- Marks are data: AnnotationShape[] (arrow / box / freehand / text) in the image's NATURAL pixels — controlled value + onChange, or uncontrolled defaultValue
- Resolution scaling: sizes are picked in display px and stored × naturalWidth / displayedWidth, so a 3240px capture shown at 660px exports with legible marks
- Select, drag to move, recolour, resize and delete an existing mark; Delete key removes it, Escape cancels the gesture or the selection
- Undo / redo over a bounded snapshot stack (historyLimit), including moves and text edits
- Text marks type in place: a caret inside the surface at the click point, rendered as SVG text — never innerHTML
- flatten() / toDataURL() through the ref handle, plus standalone flattenImageAnnotations(src, shapes) for exporting stored marks without mounting
- Cross-origin safety: crossOrigin prop, and a tainted canvas surfaces a clear error (AnnotationExportError with reason "tainted") instead of a silent failure
- Pointer events (mouse, touch and pen) with pointer capture and touch-action none while drawing
- States: loading image, image error with retry, empty (no src), readOnly (marks, no toolbar) and disabled
- i18n labels prop (ImageAnnotatorLabels — tools, swatch names, sizes, errors; markCount is a {count} template)
- ✕Capturing a signature (use SignaturePad)
- ✕General image editing — crop, filters, layers
- ✕Annotating a PDF page by page (this marks up one raster image)
Install
Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.
Usage
Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.
Props
srcImage URL (screenshot / capture). Omit for the empty state
—altAlternative text for the image
yescrossOriginCORS mode — REQUIRED to export a cross-origin image (its server must send Access-Control-Allow-Origin); without it the canvas is tainted and export reports reason "tainted"
—valueControlled marks, in the image's natural pixels
—defaultValueUncontrolled initial marks
—onChangeFires with the full next array after every committed change (draw, move, recolour, delete, undo, redo)
—toolControlled active tool
—defaultToolUncontrolled initial tool (default box)
—onToolChangeFires when the tool changes (toolbar or the v / a / b / p / t shortcuts)
—colorsInk swatches [{ value, label }] — mark colours are data baked into the export, so they are literal colours by design
—defaultColorInitial ink colour (defaults to the first swatch)
—defaultMarkSizeInitial stroke / font size in DISPLAY px, scaled to natural px on creation (default md)
—onSelectionChangeFires when the selected mark changes
—readOnlyShow the marks with no toolbar and no editing (how a filed report reads)
—disabledKeep the toolbar visible but block every edit (e.g. while submitting)
—historyLimitMaximum undo steps kept (default 100)
—toolbarEndExtra controls at the end of the toolbar, e.g. an Export button calling ref.flatten()
—emptyStateRendered instead of the default text when there is no src
—labelsOverride any built-in string (tools, colours, sizes, undo/redo, loading/error/empty, export errors) for i18n
—refImperative handle: flatten(), toDataURL(), undo(), redo(), clear(), deleteSelected(), getShapes(), focus()
—