npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-image-annotate-mngo

v1.0.102

Published

A high-performance, responsive, canvas and DOM-hybrid image annotation library built for React. It allows users to place custom icon/text elements, free-hand draw with a responsive pencil tool, manipulate elements (drag, resize, rotate, copy/paste), and u

Readme

react-image-annotate-mngo

A high-performance, responsive, canvas and DOM-hybrid image annotation library built for React. It allows users to place custom icon/text elements, free-hand draw with a responsive pencil tool, manipulate elements (drag, resize, rotate, copy/paste), and undo/redo operations with boundary clamping.

Live Demo • npm package

npm version npm downloads


Features

  • Interactive & Read-Only Modes: Supports a clean readonly viewer state that disables interaction handlers, toolbars, and selection overlays for view-only display.
  • Extensible Tool Registry: Supports custom tool definitions and SVG stamps via the tools prop for specialized diagramming workflows.
  • Resolution-Independent Coordinates: Stored annotations use normalized base coordinates, ensuring saved data renders identically across different screen resolutions and aspect ratios.
  • Boundary Clamping: All drag, resize, stamp, paste, and draw gestures are strictly bounded to the image canvas, preventing annotations from getting pushed off-screen.
  • Undo & Redo Stack: Complete immutable snapshot history with dedicated toolbar controls and hotkeys.
  • Keyboard Shortcuts: Built-in hotkeys for Undo (Ctrl/Cmd+Z), Redo (Ctrl/Cmd+Shift+Z / Ctrl+Y), Copy/Paste (Ctrl/Cmd+C, Ctrl/Cmd+V), and Delete (Delete / Backspace), with smart input focus suppression.
  • Universal Pointer Event Handling: Utilizes pointerdown, pointermove, and pointerup to deliver uniform gesture interactions across mouse, touchscreens, and styluses.
  • GPU-Accelerated CSS Transforms: Elements use transform: translate3d(...) rotate(...) instead of top/left layout properties, leveraging GPU compositing for 60fps rendering without layout reflows.
  • Direct DOM Manipulation with Commit-on-Release: Dragging, rotating, and resizing modify CSS variables directly on the element's DOM node in real-time, bypassing React re-renders during interactions and committing to the store only on pointer release (pointerup).
  • Frame-Synced Updates with requestAnimationFrame: Interactive gesture pipelines throttle visual DOM updates to the display's refresh rate via requestAnimationFrame.
  • Atomic State Management & Granular Subscriptions: Each annotation element subscribes strictly to its own ID via useSyncExternalStore, ensuring modifying or selecting one item never triggers re-renders on other items.
  • CSS Variable-Driven Container Scaling: Scale factors (--scale-x, --scale-y) are stored as CSS custom properties on the root container, allowing all annotations and strokes to scale instantly on window or container resize without iterating over individual elements.
  • Zero-Config CSS Injection: Component styles are bundled and injected automatically via JavaScript runtime — no external CSS imports required.
  • Fully Typed: Written in TypeScript with complete declarations (.d.ts) exported out of the box.

📦 Installation

npm install react-image-annotate-mngo
# or
yarn add react-image-annotate-mngo
# or
pnpm add react-image-annotate-mngo

Peer Dependencies

  • react >= 18.0.0 (Supports React 18 & 19)
  • react-dom >= 18.0.0

🚀 Quick Start

import { useState } from 'react';
import MNgoImageAnnotate, { type AnnotationData } from 'react-image-annotate-mngo';

export default function App() {
  const [annotationData, setAnnotationData] = useState<AnnotationData>({
    annotationIds: [],
    annotations: {},
    highestZIndex: 0,
  });

  return (
    <div style={{ width: '100%', height: '80vh' }}>
      <MNgoImageAnnotate
        imgSrc="https://images.unsplash.com/photo-1579783902614-a3fb3927b675?auto=format&fit=crop&w=1200&q=80"
        annotationData={annotationData}
        onAnnotationDataChange={setAnnotationData}
      />
    </div>
  );
}

Note: Give the parent container an explicit height (e.g. height: 80vh or h-screen) so the component can expand smoothly to fit your layout.


🛠️ Adding Custom Tools / Shapes

You can extend the toolbar with your own custom tools or icon stamps using the tools prop:

import MNgoImageAnnotate, { type Tool } from 'react-image-annotate-mngo';

const CUSTOM_TOOLS: Tool[] = [
  {
    name: 'server',
    btnIcon: <ServerIcon className="w-5 h-5" />, // Displayed in the toolbar
    elementIcon: <ServerIcon className="w-full h-full text-indigo-600" />, // Displayed when placed on the canvas
  },
  {
    name: 'database',
    btnIcon: <DatabaseIcon className="w-5 h-5" />,
    elementIcon: <DatabaseIcon className="w-full h-full text-emerald-600" />,
  },
];

export default function CustomToolsExample() {
  return (
    <div style={{ height: '100vh' }}>
      <MNgoImageAnnotate
        imgSrc="/architecture.png"
        tools={CUSTOM_TOOLS}
      />
    </div>
  );
}

📖 Component Props

| Prop | Type | Default | Description | |---|---|---|---| | imgSrc | string | undefined | Image URL or base64 data string to annotate. If omitted, renders an interactive grid fallback canvas. | | readonly | boolean | false | When set to true, disables interaction, dragging, tools, and shortcuts (view-only mode). | | tools | Tool[] | undefined | Custom tools/stamps added to the right side of the toolbar. | | annotationData | AnnotationData | undefined | Controlled annotation data state. | | onAnnotationDataChange | (data: AnnotationData) => void | undefined | Callback invoked whenever annotations are added, modified, moved, rotated, resized, or deleted. |


⌨️ Keyboard Shortcuts

| Shortcut | Action | Notes | |---|---|---| | Ctrl / Cmd + Z | Undo | Reverts previous annotation modification | | Ctrl / Cmd + Shift + Z | Redo | Also supports Ctrl + Y | | Ctrl / Cmd + C | Copy | Copies the currently selected element | | Ctrl / Cmd + V | Paste | Pastes the copied element with offset | | Delete / Backspace | Delete | Removes the currently selected element |

Shortcuts are automatically suppressed when typing inside <input>, <textarea>, or content-editable elements.


📐 TypeScript Interfaces

export interface Tool {
  name: string;
  btnIcon: ReactNode;
  elementIcon?: ReactNode;
}

export interface AnnotationData {
  annotationIds: string[];
  annotations: Record<string, Annotation>;
  highestZIndex: number;
}

export interface Annotation {
  id: string;
  name: string;
  zIndex: number;
  pos?: { x: number; y: number };
  rotation?: number;
  dimensions?: { width: number; height: number };
  text?: string;
  points?: number[][]; // Pencil stroke coordinates
}

export interface MNgoImageAnnotateProps {
  readonly?: boolean;
  imgSrc?: string;
  tools?: Tool[];
  annotationData?: AnnotationData;
  onAnnotationDataChange?: (newData: AnnotationData) => void;
}

🏛️ Architecture, Design Decisions & Trade-offs

  1. Semi-Controlled Hybrid Component: Annotation data lives inside an external library store, synchronized bidirectionally with the consumer app via annotationData and onAnnotationDataChange props. Supports imgSrc, tools, and readonly modes.
  2. Context Layering: Static configuration, props, global data, and refs (compRootRef, bgRef) live in context to eliminate prop drilling across deeply nested canvas elements.
  3. Dynamic Scaling Subsystem: A custom hook (useContainerScaling) computes the scale factor (ratio between rendered and natural image dimensions: currDimn / baseDimn), driving global CSS variables (--scale-x, --scale-y) via a ResizeObserver on the background element.
  4. Resolution-Independent Base Coordinates: Element positions, dimensions, rotation, and freehand pencil paths are stored normalized to the background's natural dimensions. Visual positions are dynamically computed as baseVal * scaleFactor.
  5. Hybrid DOM + Canvas Rendering: HTML5 2D Canvas is used for continuous, high-frequency freehand drawing, while native DOM elements are used for selectable, editable shape/icon annotations and text boxes.
  6. Universal Pointer Engine: Unified gesture handling across mouse, touchscreen, and stylus using pointerdown, pointermove, and pointerup.
  7. 60/120 FPS Direct DOM Mutation: During active drag, resize, or rotate gestures, updates mutate CSS transform variables directly on the DOM node inside requestAnimationFrame (bypassing React reconciliation and leveraging GPU compositing to avoid relayout/reflow and repaint cycles). State is committed to the store only on release (pointerup).
  8. External Pub/Sub Store: Implemented an external reactive store integrated into React components via useSyncExternalStore.
  9. Fine-Grained Subscriptions: Each element component subscribes strictly to its individual annotation ID, preventing whole-stage re-renders when a single element moves.
  10. Memento-Pattern History: Implemented a StateHistory class managing immutable snapshots (past, future) of annotation state for $O(1)$ undo and redo.
  1. Hybrid DOM + Canvas vs. Pure Canvas: Implementing selectable, rotatable, and editable annotations on pure Canvas requires custom hit-testing matrices, custom transform handles, synthetic text carets, and event management. The DOM handles accessibility, text input, and transforms natively.
  2. CSS Variables for Transforms: Direct CSS variable manipulation updates transforms directly on the GPU compositing layer without dirtying the React fiber tree.
  3. Element-Level ResizeObserver vs. Window Resize: The annotation canvas can resize due to sidebar toggles, flexbox shifts, or tab switches without a window resize event ever firing. ResizeObserver on the background element targets the exact source of truth.
  4. Commit-on-Release vs. Continuous Store Dispatch: Decouples visual frame updates (120Hz) from the state persistence engine, keeping the history stack clean and eliminating store dispatch lag.
  1. Custom External Store vs. Redux/Zustand:
    • Pros: Zero runtime dependencies; ultra-lightweight bundle with zero risk of peer-dependency conflicts.
    • Cons: Required writing custom subscription, selector, and notification boilerplate.
  2. Hybrid DOM + Canvas vs. Pure Canvas:
    • Pros: Native DOM accessibility, event management, and crisp SVG scaling.
    • Cons: Higher memory footprint per element than canvas pixels; requires synchronizing two distinct rendering layers (canvas & annotation shapes DOM) on resize.
  3. Full Snapshot Memento vs. Delta/Diff Actions:
    • Pros: Robust $O(1)$ time restoration without risk of state drift, missed actions, or complex inverse-matrix calculations.
    • Cons: Higher memory usage due to object duplication (mitigated by capping the history stack to 30 snapshots).
  4. Direct DOM Mutation vs. Pure React Declarative Flow:
    • Pros: Eliminates layout thrashing, virtual DOM diffing, and dropped frames during rapid pointer gestures.
    • Cons: Temporarily deviates from React’s declarative single-source-of-truth model during active gestures until settled on pointerup.

📄 License

MIT © Aditya Suman