A React renderer for Two.js — bringing declarative, component-based 2D graphics to React. Build interactive SVG, Canvas, or WebGL scenes using familiar React patterns.
npm install react-two.js react react-dom two.jsimport{Canvas,Rectangle,useFrame}from'react-two.js'functionRotatingRectangle(){constref=useRef()useFrame((t)=>ref.current.rotation=t*0.5)return<Rectangleref={ref}radius={50}fill="#00AEFF"/>}<Canvaswidth={800}height={600}autostart={true}><RotatingRectangle/></Canvas>- 🎨 Declarative 2D Graphics — Describe your Two.js scene using React components
- ⚡ Renderer Agnostic — Switch between SVG, Canvas, and WebGL without changing code
- 🪝 React Hooks — Built-in
useFramefor smooth animations anduseTwofor instance access - 📦 Fully Typed — Complete TypeScript support with proper types for all components
- 🎯 Zero Overhead — Direct mapping to Two.js primitives with no performance penalty
- 🔄 Everything Works — All Two.js features work seamlessly in React
Create complex 2D scenes using React components:
import{Canvas,Group,Rectangle,Circle,Star,useFrame}from'react-two.js'functionScene(){constgroupRef=useRef()useFrame((elapsed)=>{groupRef.current.rotation=Math.sin(elapsed)*0.5})return(<Groupref={groupRef}x={400}y={300}><Rectanglewidth={100}height={100}fill="#FF6B6B"/><Circleradius={40}fill="#4ECDC4"x={60}/><StarinnerRadius={20}outerRadius={40}sides={5}fill="#FFE66D"x={-60}/></Group>)}<Canvaswidth={800}height={600}type="webgl"><Scene/></Canvas>npm install react-two.js react react-dom two.jsRequirements as peer dependencies:
- React 19+
- Two.js v0.8.22+
Important
react-two.js is a React renderer, it must pair with a major version of React, like react-dom.
The <Canvas> component is your entry point. It creates a Two.js instance and manages the rendering context:
import{Canvas}from'react-two.js'functionApp(){return(<Canvaswidth={800}height={600}type="SVGRenderer"autostart={true}>{/* Your scene goes here */}</Canvas>)}Important
Canvas Children Restrictions: Similar to react-three-fiber, the <Canvas> component only accepts react-two.js components as children. DOM elements like <div> or <span> cannot be used inside Canvas. Place UI elements outside the Canvas:
// ✅ Correct<div><Canvas><Circleradius={50}/></Canvas><divclassName="controls">UI here</div></div>// ❌ Incorrect - will trigger warnings<Canvas><div>This will warn</div><Circleradius={50}/>
</Canvas>All Two.js primitives are available as React components:
<Canvaswidth={800}height={600}autostart={true}><Circleradius={50}fill="#00AEFF"x={400}y={300}/><Rectanglewidth={100}height={60}stroke="#FF0000"linewidth={3}/><Polygonsides={6}radius={40}fill="#00FF00"/></Canvas>The useFrame hook runs on every frame, perfect for animations:
import{useRef}from'react'import{Rectangle,useFrame}from'react-two.js'functionAnimatedRectangle(){constref=useRef()useFrame((elapsed)=>{ref.current.rotation=elapsed*0.5ref.current.scale=1+Math.sin(elapsed)*0.2})return<Rectangleref={ref}width={50}height={50}fill="#00AEFF"/>}Use useTwo to access the underlying Two.js instance:
import{useTwo}from'react-two.js'functionComponent(){const{ two, width, height }=useTwo()useEffect(()=>{if(!two)return;two.play();console.log('Canvas size:',width,height)console.log('Two.js instance:',instance)},[two])}<Canvas>— Main container that creates Two.js instance<Group>— Container for organizing and transforming multiple shapes
<Circle>— Circle with radius<Rectangle>— Rectangle with width and height<RoundedRectangle>— Rectangle with rounded corners<Ellipse>— Ellipse with width and height<Line>— Straight line between two points<Polygon>— Regular polygon with specified sides<Star>— Star shape with inner and outer radius<ArcSegment>— Arc segment with start and end angles
<Path>— Custom path with vertices<Points>— Collection of points rendered in one draw call<Text>— Text rendering
<SVG>— Load and interpret SVG files or inline SVG markup<Image>- Basic image class inspired by Figma<Sprite>— Animated sprite sheets<ImageSequence>— Animated image sequence<LinearGradient>— Linear gradient fill<RadialGradient>— Radial gradient fill<Texture>— Texture mapping
Access the Two.js instance and canvas properties:
const{ two, width, height }=useTwo()Returns:
two— The Two.js instancewidth— Canvas widthheight— Canvas height
Register a callback that runs on every animation frame:
useFrame((elapsed: number)=>{// elapsed is time in seconds since animation started})Adds zoom and pan interactions to a <Group> component. Panning is gated on registered shape hit testing so node dragging and canvas panning never conflict.
import{useRef}from'react';import{Canvas,Group,Circle,useZUI,RefGroup}from'react-two.js';functionScene(){constgroupRef=useRef<RefGroup|null>(null);constzui=useZUI(groupRef,{minZoom: 0.25,maxZoom: 8});return(<Groupref={groupRef}><Circlex={0}y={0}radius={50}fill="#00AEFF"/></Group>);}Returns ZUIControls:
controls.zoomBy(ratio, clientX?, clientY?)— Zoom relative to center or given client point.controls.zoomTo(scale, clientX?, clientY?)— Set absolute zoom scale.controls.panBy(dx, dy)— Pan by screen pixel delta.controls.reset()— Reset zoom and pan to identity state.controls.clientToSurface(clientX, clientY)— Convert screen coordinates to surface coordinates.controls.state— Ref containing{ scale, x, y }.
Subscribes to ZUI zoom and pan state updates for rendering reactive zoom UI controls.
constzui=useZUI(groupRef);const{ scale }=useZUIState(zui);All Two.js properties work as React props:
<Circleradius={50}fill="#00AEFF"stroke="#000000"linewidth={2}opacity={0.8}x={400}y={300}rotation={Math.PI/4}scale={1.5}/>Full TypeScript support with ref types for all components:
import{useRef}from'react'import{Circle,RefCircle}from'react-two.js'functionComponent(){constcircleRef=useRef<RefCircle|null>(null)useEffect(()=>{if(circleRef.current){circleRef.current.rotation=Math.PI/4}},[])return<Circleref={circleRef}radius={50}/>}functionRotatingGroup(){constref=useRef()useFrame((t)=>ref.current.rotation=t)return(<Groupref={ref}><Rectanglewidth={100}height={100}fill="#FF6B6B"/><Circleradius={50}fill="#4ECDC4"x={120}/></Group>)}function(){const[gradient,setGradient]=useState(null);constupdateRef=useMemo((ref)=>{if(ref){setGradient(ref);}},[setGradient]);return(<Canvaswidth={800}height={600}><LinearGradientref={updateRef}x1={0}y1={0}x2={100}y2={100}stops={[{offset: 0,color: '#FF6B6B'},{offset: 1,color: '#4ECDC4'}]}/><Rectanglewidth={200}height={200}fill={gradient}/></Canvas>);}Load external SVG files or use inline SVG markup with the <SVG> component:
import{SVG}from'react-two.js'// Load from external URLfunctionLogo(){return(<SVGsrc="/assets/logo.svg"x={100}y={100}onLoad={(group,svg)=>{console.log('SVG loaded with',group.children.length,'objects')}}onError={(error)=>{console.error('Failed to load SVG:',error)}}/>)}// Use inline SVG markupfunctionIcon(){return(<SVGcontent={` <svg viewBox="0 0 100 100"> <circle cx="50" cy="50" r="40" fill="#FF6B6B" /> <circle cx="35" cy="40" r="8" fill="white" /> <circle cx="65" cy="40" r="8" fill="white" /> </svg> `}x={200}y={200}scale={0.5}/>)}// Animate loaded SVGfunctionAnimatedIcon(){constsvgRef=useRef()useFrame((elapsed)=>{if(svgRef.current){svgRef.current.rotation=Math.sin(elapsed)*0.5svgRef.current.scale=1+Math.sin(elapsed*2)*0.1}})return<SVGref={svgRef}src="/icon.svg"x={400}y={300}/>}SVG Props:
src— URL to external .svg filecontent— Inline SVG markup stringx,y— Positionscale,rotation— Transform propertiesonLoad(group, svg)— Callback when SVG loads successfullyonError(error)— Callback when loading fails- All Two.js Group properties (fill, stroke, opacity, etc.)
Note
The SVG component uses Two.js's load() method which supports a subset of SVG 1.1 features. Complex SVG features like filters, animations (SMIL), and some advanced elements may not be fully supported. Refer to Two.js SVG documentation for details on supported features.
- Two.js Documentation — Complete Two.js API reference
- Two.js Examples — Interactive examples and demos
- Two.js Repository — Source code and issues
- Two.js Tutor on ChatGPT - Talk to a custom ChatGPT trained on Two.js and react-two.js
# Build the library for npm distribution
npm run build:lib
# Build the documentation site
npm run build:docs
# Preview the documentation locally
npm run preview:docs# Install dependencies
npm install
# Start development server (documentation)
npm run dev
# Run tests
npm test# Run linting
npm run lintThe development server runs the documentation site which imports the library components directly from the lib/ directory, allowing you to see changes in real-time.
Built on top of Two.js by Jono Brandel. Inspired by Three.js and react-three-fiber.