A React wrapper for the official OpenCV.js library — bring computer vision to your React apps using WebAssembly.
- Two simple primitives:
useOpenCVhook andOpenCVCanvascomponent - Lazy loading: OpenCV.js is loaded on demand, not at startup
- SSR safe: no browser APIs accessed during server render
- Singleton loading: the script is injected exactly once, regardless of how many components use it
- Dual-canvas workflow: read from an input canvas, write to an optional separate output canvas
- Memory leak prevention: clear documentation and examples for
Mat.delete()cleanup - No bundled binary: loads OpenCV.js from the official CDN by default; easy to swap for a self-hosted or contrib build
OpenCV.js is a powerful WebAssembly port of OpenCV, but integrating it into React requires managing async script loading, WASM initialization timing, canvas lifecycle, and memory cleanup. This package handles all of that so you can focus on writing your image processing logic.
This package is browser-oriented. It is not a generic Node.js image processing wrapper.
# pnpm (recommended)
pnpm add @robomous/opencv-react
# npm
npm install @robomous/opencv-react
# yarn
yarn add @robomous/opencv-reactPeer dependencies (install separately if not already present):
pnpm add react react-domimport { OpenCVCanvas } from '@robomous/opencv-react';
export default function App() {
return (
<OpenCVCanvas
src="/images/photo.jpg"
onProcess={({ canvas, cv }) => {
let src = null;
let gray = null;
try {
src = cv.imread(canvas);
gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0);
cv.imshow(canvas, gray);
} finally {
if (src) src.delete();
if (gray) gray.delete();
}
}}
/>
);
}import React, { useRef } from 'react';
import { OpenCVCanvas } from '@robomous/opencv-react';
export default function Demo() {
const outputCanvasRef = useRef<HTMLCanvasElement>(null);
return (
<div>
<OpenCVCanvas
src="/images/test.jpg"
outputCanvasRef={outputCanvasRef}
onProcess={({ canvas, cv, outputCanvas }) => {
let src = null;
let gray = null;
let dst = null;
let clahe = null;
try {
src = cv.imread(canvas);
gray = new cv.Mat();
dst = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0);
clahe = new cv.CLAHE(2.0, new cv.Size(8, 8));
clahe.apply(gray, dst);
cv.imshow(outputCanvas || canvas, dst);
} finally {
if (src) src.delete();
if (gray) gray.delete();
if (dst) dst.delete();
if (clahe) clahe.delete();
}
}}
/>
<canvas ref={outputCanvasRef} />
</div>
);
}Hook that lazily loads OpenCV.js in the browser.
const { cv, isReady, isLoading, error, load } = useOpenCV(options?);Options:
| Option | Type | Default | Description |
|---|---|---|---|
autoLoad |
boolean |
true |
Automatically load OpenCV when the component mounts |
src |
string |
vendored asset | Override the OpenCV.js script URL |
Return value:
| Field | Type | Description |
|---|---|---|
cv |
OpenCVNamespace | null |
The OpenCV namespace once ready, otherwise null |
isReady |
boolean |
Whether OpenCV is fully initialized |
isLoading |
boolean |
Whether OpenCV is currently loading |
error |
Error | null |
Load error, if any |
load |
() => Promise<void> |
Manually trigger loading |
Example:
function ProcessButton() {
const { cv, isReady, isLoading, load } = useOpenCV({ autoLoad: false });
return (
<button onClick={load} disabled={isLoading || isReady}>
{isLoading ? 'Loading OpenCV...' : 'Load OpenCV'}
</button>
);
}React component that renders an internal canvas, loads a source image, draws it, and calls your processing callback once OpenCV is ready.
<OpenCVCanvas
src="/image.jpg"
outputCanvasRef={outputRef}
autoProcess={true}
className="my-canvas"
crossOrigin="anonymous"
onLoad={({ canvas, context, image }) => { /* image drawn */ }}
onProcess={({ canvas, context, image, cv, outputCanvas }) => { /* process */ }}
onError={(error) => { /* handle error */ }}
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
src |
string |
required | Source image URL |
outputCanvasRef |
RefObject<HTMLCanvasElement | null> |
— | External canvas for processed output |
autoProcess |
boolean |
true |
Call onProcess automatically when ready |
className |
string |
— | CSS class for the internal canvas |
crossOrigin |
'' | 'anonymous' | 'use-credentials' |
— | Cross-origin attribute for the image |
onLoad |
(payload: LoadPayload) => void |
— | Called after the image is drawn |
onProcess |
(payload: ProcessPayload) => void | Promise<void> |
— | Called when OpenCV is ready and image is drawn |
onError |
(error: Error) => void |
— | Called on image load or processing errors |
Process payload:
interface ProcessPayload {
canvas: HTMLCanvasElement; // internal input canvas (image is drawn here)
context: CanvasRenderingContext2D;
image: HTMLImageElement;
cv: OpenCVNamespace; // the OpenCV instance
outputCanvas?: HTMLCanvasElement; // external output canvas, if provided
}Important: Always call mat.delete() for every Mat you create inside onProcess to prevent memory leaks. Use a try/finally block as shown in the examples.
@robomous/opencv-react does not bundle an OpenCV.js binary. By default it loads from the official CDN:
https://docs.opencv.org/4.13.0/opencv.js
To use a different version, a contrib build, or a self-hosted file, pass the src option:
// Specific version from the official CDN
useOpenCV({ src: 'https://docs.opencv.org/4.10.0/opencv.js' })
// Self-hosted contrib build
useOpenCV({ src: '/assets/opencv-4.13.0-contrib.min.js' })
// Same option on OpenCVCanvas
<OpenCVCanvas src={imageUrl} opencvSrc="/assets/opencv.js" onProcess={...} />Contrib modules (SIFT, SURF, etc.) are not available on the official CDN. Build them locally using Docker and the included build script:
# Standard build
pnpm run build:opencv:release -- --version 4.13.0
# Contrib build
pnpm run build:opencv:release -- --version 4.13.0 --with-contrib
# With separate .wasm file
pnpm run build:opencv:release -- --version 4.12.0 --disable-single-fileSupported versions: 4.10.0, 4.11.0, 4.12.0, 4.13.0. Output is written to build/opencv-js/releases/. Non-release versions (branches, SHAs, main) are rejected. To add a new release, add it to SUPPORTED_RELEASES in scripts/helpers/opencv-release-config.sh.
git clone https://github.com/Robomous/opencv-react.git
cd opencv-js-react
pnpm install
# Run tests
pnpm run test
# Build the library
pnpm run build
# Typecheck
pnpm run typecheck
# Lint
pnpm run lintWatch mode:
pnpm run dev # tsup watch
pnpm run test:watch # vitest watchpnpm run test # run all tests once
pnpm run test:watch # run in watch modeTests use Vitest with Testing Library and jsdom.
Publishing is triggered automatically when a GitHub release is published. The release workflow runs lint, typecheck, tests, and build, then publishes to npm using trusted publishing (OIDC). No OpenCV.js binary is required or included.
To configure npm trusted publishing:
- See npm trusted publishers documentation
- Enable it in your npm package settings for the
Robomous/opencv-reactGitHub repository
Manual publish:
pnpm run prepack # runs typecheck + test + build
pnpm publish --access public- Fork and clone the repository
- Install dependencies:
pnpm install - Make your changes
- Run tests before opening a PR:
pnpm run test - Keep public API changes documented in the README
- Open a pull request — CI runs automatically
If you modify the Bash build workflow, preserve the release-only version validation and shallow-clone semantics. Non-release versions (branches, SHAs, main) must continue to be rejected.
PRs trigger CI automatically. Merging to main + publishing a GitHub release triggers the npm publish.
Apache-2.0 — see LICENSE for details.
This package does not bundle OpenCV.js. The default CDN URL points to the official OpenCV.js builds hosted by the OpenCV project. When building a local artifact using the included scripts, it is compiled from the official OpenCV source repository via platforms/js/build_js.py and Emscripten. No modifications are made to the OpenCV source. OpenCV is licensed under the Apache 2.0 license.