@stargiraffe/annotator
v0.3.22
Published
React text highlighting & annotation library with localStorage persistence
Maintainers
Readme
@stargiraffe/annotator
A lightweight React library for text highlighting and annotations with localStorage persistence.
Features
- ✨ Easy text selection - Select text to create highlights
- 🎨 Multiple highlight colors - yellow, green, blue, pink
- 📝 Annotations - Add notes to highlights
- 💾 Persistent storage - localStorage integration (fully customizable)
- ♿ Accessible - ARIA labels and keyboard navigation
- 📦 Tree-shakeable - Import only what you need (~47KB gzipped)
- 🎯 TypeScript - Full type safety with Zod schemas
- ⚡ No external dependencies - Only React peer dependency
Installation
npm install @stargiraffe/annotator react react-domQuick Start
Basic Usage
Pass your content as JSX children:
import { HighlightableContent } from '@stargiraffe/annotator'
function MyComponent() {
return (
<HighlightableContent id="my-content">
<p>Your text content here...</p>
</HighlightableContent>
)
}With Markdown Content
import { HighlightableContent } from '@stargiraffe/annotator'
import MarkdownRenderer from '@/components/ui/markdown-renderer'
function MyComponent() {
return (
<HighlightableContent id="markdown-content">
<MarkdownRenderer content="# Your **markdown** content" />
</HighlightableContent>
)
}Using Hooks Directly
import { useHighlighter, useAnnotations } from '@stargiraffe/annotator'
function MyComponent() {
const { highlights, addHighlightWithColor, removeHighlightById } = useHighlighter({
id: 'my-context-id',
})
const { annotations, createAnnotation, deleteAnnotation } = useAnnotations({
id: 'my-context-id',
})
return (
<div>
<p>Highlights: {highlights.length}</p>
<p>Annotations: {annotations.length}</p>
</div>
)
}API
Components
HighlightableContent
Main wrapper component that makes children content highlightable.
Props:
interface HighlightableContentProps {
id: string // Unique identifier for this context
children: ReactNode // JSX content to be highlightable
textSize?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' // Text size (default: 'base')
className?: string // Additional CSS classes
}Example:
<HighlightableContent id="question-123" textSize="lg">
<div>Your content here</div>
</HighlightableContent>Hooks
useHighlighter
Manages highlight state and operations.
Props:
interface UseHighlighterProps {
id: string // Unique identifier for this highlight context
}Returns:
interface UseHighlighterReturn {
highlights: Highlight[]
addHighlightWithColor: (text: string, range: HighlightRange, color: HighlightColor) => string | null
changeHighlightColor: (highlightId: string, color: HighlightColor) => void
removeHighlightById: (highlightId: string) => void
clearAllForCurrentContext: () => void
}useAnnotations
Manages annotation state and operations.
Props:
interface UseAnnotationsProps {
id: string // Unique identifier for this annotation context
}Returns:
interface UseAnnotationsReturn {
annotations: Annotation[]
createAnnotation: (text: string, highlightId: string | null) => void
updateAnnotationText: (annotationId: string, text: string) => void
deleteAnnotation: (annotationId: string) => void
deleteAnnotationsForHighlight: (highlightId: string) => void
getAnnotationsForHighlight: (highlightId: string) => Annotation[]
clearAllAnnotationsForCurrentContext: () => void
}Types
export type HighlightColor = 'yellow' | 'green' | 'blue' | 'pink'
export interface Highlight {
id: string
text: string
range: HighlightRange
color: HighlightColor
createdAt: number
}
export interface Annotation {
id: string
highlightId: string | null
text: string
createdAt: number
updatedAt: number
}
export interface HighlightRange {
startOffset: number
endOffset: number
textContent: string
startIndex?: number
endIndex?: number
}Storage
Highlights and annotations are persisted to browser localStorage by default using this key format:
highlights-{contextId}
annotations-{contextId}Storage Functions
Import and use storage functions directly:
import {
saveHighlights,
loadHighlights,
removeHighlight,
clearAllHighlights,
updateHighlightColor,
saveAnnotations,
loadAnnotations,
addAnnotation,
removeAnnotation,
clearAllForId,
} from '@stargiraffe/annotator/storage'Styling
This library uses Tailwind CSS utility classes. Ensure your project has Tailwind configured:
// tailwind.config.js
module.exports = {
content: [
'./node_modules/@stargiraffe/annotator/**/*.{js,ts,jsx,tsx}',
// ... your other paths
],
}Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Limitations
- Highlights may not persist if DOM content changes significantly
- Complex nested structures may affect highlight accuracy
- localStorage has ~5-10MB limit per domain
API Design Philosophy
The HighlightableContent component uses the children prop pattern for maximum flexibility:
- Consumers pass pre-rendered JSX as children
- The component handles only highlighting and annotations logic
- Content rendering is completely decoupled from highlighting
This approach:
- ✅ Supports any content type (markdown, HTML, formatted text, etc.)
- ✅ Eliminates wrapper components
- ✅ Follows React best practices
- ✅ Enables tree-shaking and code splitting
License
MIT
Contributing
See CONTRIBUTING.md for guidelines.
