Recogito Text Annotator
April 30, 2026 ยท View on GitHub
A JavaScript library for adding interactive text annotation functionality to web applications.
Also available: React wrapper | TEI/XML extension | Recogito PDF Annotator

Installation
npm install @recogito/text-annotator
Quick Start
import { createTextAnnotator } from '@recogito/text-annotator';
import '@recogito/text-annotator/text-annotator.css';
const anno = createTextAnnotator(document.getElementById('content'));
// Load annotations from a file
anno.loadAnnotations('annotations.json');
// Listen to user events
anno.on('createAnnotation', annotation => {
console.log('new annotation', annotation);
});
Configuration Options
const anno = createTextAnnotator(element, options);
| Option | Type | Default | Description |
|---|---|---|---|
allowModifierSelect | boolean | false | Allows users to extend an existing annotation by holding Ctrl (Cmd on Mac) while selecting. |
annotatingEnabled | boolean | true | Enable or disable creation of new annotations. |
dismissOnNotAnnotatable | 'NEVER' | 'ALWAYS' | function | 'NEVER' | Controls whether the currently selected annotation is un-selected when clicking outside the annotatable area. |
mergeHighlights | object | undefined | Merge adjacent highlights. Options: horizontalTolerance and verticalTolerance (pixels) |
selectionMode | 'shortest' | 'all' | 'shortest' | When selecting overlapping annotations: select all or only the shortest. |
style | HighlightStyleExpression | undefined | Custom highlight styling function. |
user | User | anonymous guest | Current user info, automatically added to new or updated annotations. |
userSelectAction | UserSelectActionExpression | SELECT | Behavior when the user selects an annotation (see below) |
Annotator API
Methods
addAnnotation(annotation)
Add a single annotation programmatically.
anno.addAnnotation(annotation);
cancelSelected()
Programmatically cancel the current selection.
anno.cancelSelected();
canRedo() / canUndo()
Test if there are any re- or undoable user actions in the undo stack.
const canRedo = anno.canRedo();
clearAnnotations()
Remove all annotations.
anno.clearAnnotations();
destroy()
Destroy the annotator instance and clean up all event listeners.
anno.destroy();
getAnnotationById(id)
Retrieve a specific annotation.
const annotation = anno.getAnnotationById('annotation-id');
getAnnotations()
Return all annotations.
const annotations = anno.getAnnotations();
getSelected()
Return currently selected annotations.
const selected = anno.getSelected();
getUser()
Return the current user.
const user = anno.getUser();
loadAnnotations(url, replace = true)
Load annotations from a URL.
await anno.loadAnnotations('/annotations.json');
redo() / undo()
Programmatically redo or undo the last user edit.
anno.undo();
anno.redo();
removeAnnotation(annotationOrId)
Delete an annotation by ID or object.
anno.removeAnnotation('annotation-id');
scrollIntoView(annotationOrId)
Scroll the annotation into view. Returns true if successful, false if annotation is not currently rendered.
anno.scrollIntoView('annotation-id');
setAnnotatingEnabled(enabled)
Enable or disable annotation creation.
anno.setAnnotatingEnabled(false);
setAnnotatingMode(mode)
Configure the annotation mode:
CREATE_NEW(default): each user selection creates a new annotation.ADD_TO_CURRENT: the user selection extends the currently selected annotation's range, if any.REPLACE_CURRENT: the user selection replaces the currently selected annotations' range, if any.
anno.setAnnotatingMode('ADD_TO_CURRENT');
setAnnotations(annotations, replace = true)
Bulk-add annotations. If replace is true (default), all existing annotations are deleted first. If false, the new annotations are appended to existing ones.
anno.setAnnotations(annotations);
setFilter(filter)
Apply a filter function to control which annotations are displayed.
anno.setFilter(annotation =>
annotation.bodies.some(b => b.purpose === 'commenting')
);
setSelected(idOrIds)
Programmatically select annotation(s). Passing undefined or no argument will clear selection.
anno.setSelected('annotation-id');
anno.setSelected(['id-1', 'id-2']);
setStyle(style)
Change the highlighting style function or set a static style - see details below.
anno.setStyle({
fill: 'yellow',
fillOpacity: 0.25
});
anno.setStyle((annotation, state) => ({
fill: annotation.bodies[0]?.purpose === 'tagging' ? 'yellow' : 'lightblue',
fillOpacity: state.hovered ? 0.5 : 0.25
}));
setUser(user)
Set the current user.
anno.setUser({ id: 'new-user@example.com', name: 'John' });
setUserSelectAction(action)
Change the current userSelectAction, which determines what happens when the user selects an annotation interactively. Can be a UserSelectAction, or a function that receives the annotation as input and returns a UserSelectAction.
- SELECT (default): The annotation will be selected and the
selectionChangedevent will be triggered. - NONE: the annotation is inert, clicking has no effect.
anno.setUserSelectAction('NONE');
anno.setUserSelectAction(annotation => annotation.bodies.length > 0 ? 'SELECT' : 'NONE');
setVisible(visible)
Show or hide all annotations.
anno.setVisible(false);
updateAnnotation(updated)
Update an existing annotation.
anno.updateAnnotation(updated);
Events
Listen to annotation lifecycle events using on() and remove listeners with off().
createAnnotation
Fired when the user creates a new annotation.
// Example: save new annotations to a backend
anno.on('createAnnotation', annotation => {
console.log('Created:', annotation);
fetch('/my-api/annotations', {
method: 'POST',
body: JSON.stringify(annotation)
});
});
updateAnnotation
Fired when the user updates an annotation. Receives the updated annotation and the previous version.
anno.on('updateAnnotation', (annotation, previous) => {
console.log('Updated:', annotation, 'was:', previous);
});
deleteAnnotation
Fired when the user deletes an annotation.
anno.on('deleteAnnotation', annotation => {
console.log('Deleted:', annotation);
});
selectionChanged
Fired when the selection was changed by the user.
anno.on('selectionChanged', annotations => {
console.log('Selected:', annotations);
});
viewportIntersect
Fired when annotations enter or leave the visible area.
anno.on('viewportIntersect', annotations => {
console.log('Visible:', annotations);
});
Remove event listeners:
const handler = annotation => console.log(annotation);
anno.on('createAnnotation', handler);
anno.off('createAnnotation', handler);
Annotation Data Model
The Text Annotator data model aligns closely with the W3C Web Annotation Data Model, but with a few key differences to optimize for performance and ease of use. Every annotation in Annotorious is represented by a JavaScript object with the following structure:
{
"id": "67a427d7-afc3-474a-bdab-1e2ea8dc78f6",
"bodies": [],
"target": {
"selector": [{
"quote": "Tell me, O muse",
"start": 48,
"end": 63
}],
"creator": {
"id": "zD62eVrpvJgMEEWuPpPS"
},
"created": "2025-09-30T07:28:54.973Z",
"updated": "2025-09-30T07:28:56.158Z"
}
}
-
id- a unique identifier for the annotation. The ID can be any alphanumeric string. Annotations created by users will receive a globally unique UUID automatically. -
target- the target represents the text range that the annotation is associated with. Theselectorprovides the selectedquoteand character offsets forstartandend. -
bodiesare designed to carry application-specific payload, such as comments, tags, or other metadata associated with the annotation.
Annotation Styling
You can customize the appearance of highlights using the style config option or setStyle() method.
const anno = createTextAnnotator(element, {
style: {
fill: '#ffeb3b',
fillOpacity: 0.25,
underlineStyle: 'dashed',
underlineColor: '#7d7208ff',
underlineOffset: 0,
underlineThickness: 2
}
});
You can provide a function to style annotations based on their properties. The style function receives three arguments:
annotation- the annotation objectstate- an object withselectedandhoveredboolean propertieszIndex- the stacking order (useful for layering effects on overlapping annotations)
anno.setStyle((annotation, state, zIndex) => {
const hasTag = annotation.bodies.some(b => b.purpose === 'tagging');
return {
fill: hasTag ? '#ffeb3b' : '#bbdefb',
fillOpacity: state.hovered ? 0.35 : 0.2,
underlineColor: hasTag ? '#f57f17' : undefined
};
});
License
The Recogito Text Annotator is licensed under the BSD 3-Clause license.