React + Canvas = Love. JavaScript library for drawing complex canvas graphics using React.
React Konva

React Konva is a JavaScript library for drawing complex canvas graphics using React.
It provides declarative and reactive bindings to the Konva Framework.
OPEN DEMO
An attempt to make React work with the HTML5 canvas library. The goal is to have similar declarative markup as normal React and to have similar data-flow model.
At the current moment, react-konva is not supported in React Native environment.
Currently you can use all Konva components as React components and all Konva events are supported on them in same way as normal browser events are supported.
Installation
npm install react-konva konva --save
Tutorials and Documentation
Example
import React, { useState } from 'react';
import { render } from 'react-dom';
import { Stage, Layer, Rect, Text } from 'react-konva';
import Konva from 'konva';
const ColoredRect = () => { const [color, setColor] = useState('green');
const handleClick = () => { setColor(Konva.Util.getRandomColor()); };
return <Rect x={20} y={20} width={50} height={50} fill={color} shadowBlur={5} onClick={handleClick} />; };
const App = () => { return ( <Stage width={window.innerWidth} height={window.innerHeight}> <Layer> <Text text="Try click on rect" /> <ColoredRect /> </Layer> </Stage> ); };
render(<App />, document.getElementById('root'));
To get more info about Konva you can read Konva Overview.
Actually you don't need to learn react-konva. Just learn Konva framework, you will understand how to use react-konva
Core API
react-konva supports all shapes, that Konva supports with the same names, and also it supports all the same events like click, touchmove, dragend, etc with "on" prefix like onClick, onTouchMove, onDragEnd.
Getting reference to Konva objects
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" />; };
Strict mode
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.
Minimal bundle
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';</code></pre>
Demo: https://codesandbox.io/s/6l97wny44z
Usage with Next.js
From
konva@10.0.0, you don't need to do any extra steps. It will work out-of-the-box.
If you use konva verion <= 9, continue reading.
Note:
react-konva is designed to work in the client-side. On the server side, it will render just empty div. So it doesn't make much sense to use react-konva for server-side rendering. In Next.js you may have issue like
> Module not found: Can't resolve 'canvas'
Why do we see this error?
canvas module is used for canvas rendering in Node.JS environment. konva library will use it there, but it doesn't have this dependency explicitly.
How to solve this issue? There are two approaches:
Approach 1: manually install canvas module
You can install
canvas module manually.
<pre><code class="lang-bash">npm install canvas@next</code></pre>
The solution will solve the issue, but it will have unnecessary dependency on
canvas module which may increase build time a little bit.
Approach 2: Use dynamic import
Next.js docs: https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading
With this approach your canvas component will be loaded on the client-side only. So you will not have any issues with server-side rendering. Also
next.js will automatically understand that it doesn't need to load canvas module, because it is used for server-side rendering only.
Step 1 - Create canvas component
You need to define your canvas components somewhere in your
components folder.
It must be placed outside of
pages or app folder (because they are used for server rendering).
Your
components/canvas.js file may look like this:
<pre><code class="lang-js">import { Stage, Layer, Circle } from 'react-konva';
function Canvas(props) { return ( <Stage width={window.innerWidth} height={window.innerHeight}> <Layer> <Circle x={200} y={100} radius={50} fill="green" /> </Layer> </Stage> ); }
export default Canvas;</code></pre>
Step 2 - Use dynamic import
Then you can use it in your page. Notice, it is imported to have
'use client';.
<pre><code class="lang-js">'use client'; import dynamic from 'next/dynamic';
const Canvas = dynamic(() => import('../components/canvas'), { ssr: false, });
export default function Page(props) { return <Canvas />; }</code></pre>
Step 3 - Setup next.config.js
- In some versions of next.js you may need to set up
next.config.js to make it work:
<pre><code class="lang-js">/* @type {import('next').NextConfig} /
const nextConfig = {
webpack: (config) => {
config.externals = [...config.externals, { canvas: 'canvas' }]; // required to make Konva & react-konva work
return config;
},
};
module.exports = nextConfig;</code></pre>
If you are using
turbopack you may need to use this configuration:
<pre><code class="lang-js">/* @type {import('next').NextConfig} / const nextConfig = { experimental: { turbo: { resolveAlias: { canvas: './empty.js', }, }, }, };
module.exports = nextConfig;</code></pre>
Also create empty file
empty.js in the root of your project. That file will be used as replacement for canvas library. As we don't really need to use it, it will be just empty file.
Usage with React Context
Note: this section may be not relevant, because this issue was fixed in
react-konva@18.2.2. So context should work by default.
Due to a known issue with React, Contexts are not accessible by children of the react-konva
Stage component. If you need to subscribe to a context from within the Stage, you need to "bridge" the context by creating a Provider as a child of the Stage. For more info, see this discussion and this react-redux demo. Here is an example of bridging the context (live demo):
<pre><code class="lang-js">import React, { Component } from 'react'; import Konva from 'konva'; import { render } from 'react-dom'; import { Stage, Layer, Rect } from 'react-konva';
const ThemeContext = React.createContext('red');
const ThemedRect = () => { const value = React.useContext(ThemeContext); return <Rect x={20} y={50} width={100} height={100} fill={value} shadowBlur={10} />; };
const Canvas = () => { return ( <ThemeContext.Consumer> {(value) => ( <Stage width={window.innerWidth} height={window.innerHeight}> <ThemeContext.Provider value={value}> <Layer> <ThemedRect /> </Layer> </ThemeContext.Provider> </Stage> )} </ThemeContext.Consumer> ); };
class App extends Component { render() { return ( <ThemeContext.Provider value="blue"> <Canvas /> </ThemeContext.Provider> ); } }</code></pre>
Comparisons
react-konva vs react-canvas
react-canvas is a completely different react plugin. It allows you to draw DOM-like objects (images, texts) on canvas element in very performant way. It is NOT about drawing graphics, but react-konva is exactly for drawing complex graphics on