React Konva provides declarative React components for the Konva 2D canvas scene graph. Use it to build design editors, whiteboards, diagrams, annotations, and other interactive graphics.
React Konva is MIT licensed. It supports Konva shapes and events. It does not support React Native.
React Konva 19.3 requires React and React DOM ^19.3.0.
For React 19.2, install the latest React Konva 19.2 release.
For React 18, install the latest React Konva 18 release.
npm install react-konva konvaimport {useState} from 'react';
import {Stage, Layer, Rect} from 'react-konva';
export default function App() {
const [color, setColor] = useState('#00a8e8');
return (
<Stage width={600} height={400}>
<Layer>
<Rect
x={50} y={50} width={120} height={80}
fill={color} draggable
onClick={() => setColor(color === '#00a8e8' ? '#ff7a00' : '#00a8e8')}
/>
</Layer>
</Stage>
);
}Building a full design editor? Polotno is a commercial design editor SDK built on Konva by the Konva maintainers. It ships templates, text editing, and export, so you integrate an editor instead of building one:
npm install polotno.
To get more info about Konva you can read
Konva Overview.
react-konva follows the Konva API. Learn the Konva scene graph, properties, and events before you add framework-specific patterns.
react-konva supports Konva shapes with the same names. Event props use React names such as onClick, onTouchMove, and onDragEnd.
To get reference of Konva instance of a node you can use ref property.
import React, { useEffect, useRef } from 'react';
const MyShape = () => {
const circleRef = useRef();
useEffect(() => {
// log Konva.Circle instance
console.log(circleRef.current);
}, []);
return <Circle ref={circleRef} radius={50} fill="black" />;
};useActionState and useOptimistic work inside a Stage.
Fragments group Konva children, but Fragment refs remain null because the
canvas renderer has no DOM Fragment instance.
ViewTransition is not supported inside a Stage.
To animate the Stage's DOM container, place ViewTransition around Stage in
the React DOM tree. Individual Konva shapes have no DOM elements for view transitions.
useFormStatus reads the surrounding DOM form's status inside Stage.
Place error boundaries inside Stage and use Konva elements for their fallbacks.
Uncaught canvas errors appear in the console with the error and component stack.
DOM error boundaries around Stage do not catch errors from canvas children.
Place Suspense inside Stage to show a Konva fallback for canvas children.
For a DOM loading indicator, read the promise in a component above Stage.
The DOM and canvas use separate React roots, so their Suspense boundaries and
transition pending states do not propagate between roots.
Activity and Suspense around Stage preserve canvas component state when they
hide and reveal an existing Stage. An initially hidden Stage creates its canvas
on the first reveal.
React's StrictMode also enables development checks inside Stage.
For hook coverage and renderer limits, see the React compatibility matrix.
By default react-konva works in "non-strict" mode. If you changed a property manually (or by user action like drag&drop) properties of the node will be not matched with properties from render(). react-konva updates ONLY properties changed in render().
In strict mode react-konva will update all properties of the nodes to the values that you provided in render() function, no matter changed they or not.
You should decide what mode is better in your actual use case.
To enable strict mode globally you can do this:
import { useStrictMode } from 'react-konva';
useStrictMode(true);Or you can enable it only for some components:
<Rect width={50} height={50} fill="black" _useStrictMode />Take a look into this example:
import { Circle } from 'react-konva';
import Konva from 'konva';
const Shape = () => {
const [color, setColor] = React.useState();
return (
<Circle
x={0}
y={0}
draggable
radius={50}
fill={color}
onDragEnd={() => {
setColor(Konva.Util.getRandomColor());
}}
/>
);
};The circle is draggable and it changes its color on dragend event. In strict mode position of the node will be reset back to {x: 0, y: 0} (as we defined in render). But in non-strict mode the circle will keep its position, because x and y are not changed in render.
By default react-konva imports full Konva version. With all the shapes and all filters. To minimize bundle size you can use minimal core version of react-konva:
// load minimal version of 'react-konva`
import { Stage, Layer, Rect } from 'react-konva/lib/ReactKonvaCore';
// minimal version has NO support for core shapes and filters
// if you want import a shape into Konva namespace you can just do this:
import 'konva/lib/shapes/Rect';Demo: https://codesandbox.io/s/6l97wny44z
Konva 10+ works with Next.js without extra canvas setup. Use a
Client Component ('use client').
Konva 9 and earlier need extra setup, such as installing canvas or
disabling SSR for the canvas component.
Components inside Stage receive React contexts from its parent tree automatically. This behavior is available since react-konva@18.2.2.
import React from 'react';
import { Stage, Layer, Rect } from 'react-konva';
const ThemeContext = React.createContext('red');
function ThemedRect() {
const fill = React.useContext(ThemeContext);
return <Rect width={100} height={100} fill={fill} />;
}
function App() {
return (
<ThemeContext.Provider value="blue">
<Stage width={300} height={200}>
<Layer>
<ThemedRect />
</Layer>
</Stage>
</ThemeContext.Provider>
);
}With Konva 10.5+, native input batches ordinary React state updates per Konva
listener and commits them before that listener returns, keeping DOM and canvas in sync.
This includes drag and transform events. To batch MobX reactions too, pass runInAction:
import { runInAction } from 'mobx';
<Stage eventBatchFunc={runInAction} width={600} height={400}>
{/* Existing layers, shapes, and handlers */}
</Stage>The wrapper must call its callback exactly once, synchronously. Older Konva versions use normal React scheduling and ignore this prop. See the release notes for performance and compatibility details.
Run npm install and npx playwright install chromium before the tests.
npm testruns browser tests with development and production React, performance counts, package builds, server rendering, and TypeScript checks.BROWSER=firefox npm testandBROWSER=webkit npm testselect other installed Playwright browsers.npm run test:performanceruns commit and calculation limits with production profiling builds.npm run test:ssrchecks all built entry points without a DOM or native canvas backend, including imports when the optional event hook is absent.npm run test:typingschecks the source and consumer examples.npm run bench:events -- HEADcompares elapsed interaction time against a commit. See the benchmark guide.
All tests use the latest published Konva. Compatibility tests disable its optional hook; they do not install historical versions. CI runs the full suite in Chromium, Firefox, and WebKit. A separate job checks minimum React with latest Konva. Run development, production, and profiling suites sequentially because they share Vite's dependency cache.
All interaction regressions run in the regular suite. There is no separate expected-failure command.
See the Konva demos for more complete examples.
