A JavaScript library for adding interactive text annotation functionality to web applications.
Also available: React wrapper | TEI/XML extension | Recogito PDF Annotator
npm install @recogito/text-annotator
import { createTextAnnotator } from '@recogito/text-annotator';
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);
});
const anno = createTextAnnotator(element, options);
Option | Type | Default | Description |
---|---|---|---|
annotatingEnabled |
boolean |
true |
Enable or disable interactive creation of new annotations. |
dismissOnNotAnnotatable |
'NEVER' | 'ALWAYS' | function |
'NEVER' |
Controls whether the current selection is dismissed when clicking outside of annotatable content. |
selectionMode |
'shortest' | 'all' |
'shortest' |
When the user selects overlapping annotations: select all or only the shortest. |
style |
HighlightStyleExpression |
undefined |
Custom styling function for highlights. |
user |
User |
anonymous guest | Current user information, automatically added to created or updated annotations. |
Returns all annotations.
const annotations = anno.getAnnotations();
Returns the annotations with the given ID.
const annotation = anno.getAnnotationById('annotation-id');
Bulk-adds annotations. If replace
is true
(default), all existing annotations are removed first. If false
, the new annotations are appended to existing ones.
anno.setAnnotations(annotations);
Loads annotations from a URL.
await anno.loadAnnotations('/annotations.json');
Adds a single annotation programmatically.
anno.addAnnotation(annotation);
Updates an existing annotation. (The original annotation with the same ID will be replaced.)
anno.updateAnnotation(updated);
Removes an annotation by object or ID.
anno.removeAnnotation('annotation-id');
Removes all annotations.
anno.clearAnnotations();
Returns currently selected annotations.
const selected = anno.getSelected();
Programmatically select annotation(s). Passing undefined
or no argument will clear selection.
anno.setSelected('annotation-id');
anno.setSelected(['id-1', 'id-2']);
Programmatically cancel the current selection.
anno.cancelSelected();
Scrolls the annotation into view. Returns true
if successful, false
if annotation is not currently rendered.
anno.scrollIntoView('annotation-id');
Returns the current user.
const user = anno.getUser();
Sets the current user.
anno.setUser({ id: '[email protected]', name: 'John' });
Applies a filter function to control which annotations are displayed.
anno.setFilter(annotation =>
annotation.bodies.some(b => b.purpose === 'commenting')
);
Updates the highlighting style function.
anno.setStyle(annotation => ({
fill: annotation.bodies[0]?.purpose === 'tagging' ? 'yellow' : 'lightblue',
fillOpacity: 0.25
}));
Shows or hides all annotations.
anno.setVisible(false);
Programmatically undo the last user edit.
anno.undo();
Programmatically redo the last undone user edit.
anno.redo();
Destroys the annotator instance and cleans up all event listeners.
anno.destroy();
Listen to annotation lifecycle events using on()
and remove listeners with off()
.
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)
});
});
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);
});
Fired when the user deletes an annotation.
anno.on('deleteAnnotation', annotation => {
console.log('Deleted:', annotation);
});
Fired when the selection was changed by the user.
anno.on('selectionChanged', annotations => {
console.log('Selected:', annotations);
});
Remove event listeners:
const handler = annotation => console.log(annotation);
anno.on('createAnnotation', handler);
anno.off('createAnnotation', handler);
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. Theselector
provides the selectedquote
and character offsets forstart
andend
. -
bodies
are designed to carry application-specific payload, such as comments, tags, or other metadata associated with the annotation.
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 withselected
andhovered
boolean 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
};
});
The Recogito Text Annotator is licensed under the BSD 3-Clause license.