New to Rust? Grab our free Rust for Beginners eBook Get it free →
Fabric.js Tutorial: Build interactive canvas graphics in JavaScript

Fabric.js is a free open-source JavaScript library for drawing and manipulating shapes, text and images on an HTML5 canvas. It wraps native canvas in an object model, so every shape becomes reusable and editable instead of a one-off drawing command.
Why do we need fabric.js?
Fabric.js makes it easy to work with HTML5 canvas elements. With it, you can create simple shapes like rectangles and circles, as well as more complex things like text and images. You can move, resize, and rotate these shapes by dragging with your mouse, making it great for interactive apps. It also supports features like grouping objects, animations, and colour changes. Plus, it works on mobile devices with touch support. Overall, fabric.js saves time and effort when creating cool graphics for websites or apps.
Created by Juriy Zaytsev around 2010, fabric.js is now one of the two most actively maintained canvas libraries on GitHub (alongside Konva.js), with tens of thousands of stars.
Let’s compare creating a circle using the basic HTML5 canvas code and fabric.js code. In a vanilla HTML5 canvas, we need to define and draw the circle by ourselves. Like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Circle</title>
</head>
<body>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;"></canvas>
<script>
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
//Here we Draw a circle
ctx.beginPath();
ctx.arc(150, 150, 50, 0, 2 * Math.PI); // (x, y, radius, startAngle, endAngle)
ctx.fillStyle = "blue";
ctx.fill();
ctx.stroke();
</script>
</body>
</html>
In this code, we have to first access the canvas content and then start drawing a path. We use the arc() function to create a circle, and then we set the colour and fill the shape.
Output:

Now if we use fabric.js, then creating a circle would be simpler and interactive. Let’s see how:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Circle with Fabric.js</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">
</script>
<body>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;"></canvas>
<!-- script after Fabric.js is loaded -->
<script>
window.onload = function () {
// Create a fabric.js canvas
const canvas = new fabric.Canvas('myCanvas');
// Create a blue circle
const circle = new fabric.Circle({
radius: 50,
fill: 'blue',
left: 100,
top: 100
});
// Add the circle to the canvas
canvas.add(circle);
};
</script>
</body>
</html>
In this code:
- The script tag (<script src=”…fabric.min.js”>) loads Fabric.js.
- After the page loads, Fabric.js creates a canvas (new fabric.Canvas(‘myCanvas’)). This tells Fabric.js to control the HTML canvas with the ID “myCanvas.“
- Fabric.js is then used to create a blue circle (new fabric.Circle({…})) with a radius of 50, placed at coordinates (100, 100).
- The circle is added to the Fabric.js controlled canvas using canvas.add(circle).
Output:

Features of fabric.js
- Shape Control: Easily create, resize, and rotate shapes.
- Grouping: Combine multiple shapes to move or transform together.
- Mobile Support: Works smoothly on mobile devices with touch support.
- Save & Load: Quickly convert canvas designs to JSON and reload them.
- Text & Shapes: Add and edit text, simple shapes, and complex designs.
- Gradients & Filters: Apply colour gradients and image filters.
- Animations: Provides built-in support for animating objects.
- Object Interaction: Move, lock, flip, or drag objects as needed.
- Free Drawing & Erasing: Draw freely on the canvas and erase parts.
- Image & Format Support: Works with JPG, PNG, JSON, and SVG formats.
How to install fabric.js
There are many ways by which you can install Fabric.js into your system:
1. NPM: If you are creating a Node.js project, you can install Fabric.js using NPM.
npm install fabric
2. Yarn: It is another package manager like NPM. We can install Fabric.js using Yarn as well.
yarn install fabric
3. CDN Link: Using CDN, we don’t need to install anything. We can just link to Fabric.js in our HTML with a <script> tag.
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
These examples use fabric 3.x syntax (new fabric.Canvas(...)), which still works. A fresh npm install fabric today pulls v7, which uses named ESM imports instead of the global fabric namespace:
// fabric v6/v7 (ESM)
import { Canvas, Rect, Circle } from 'fabric';
const canvas = new Canvas('myCanvas');
const circle = new Circle({ radius: 50, fill: 'blue', left: 100, top: 100 });
canvas.add(circle);
To match the syntax on this page exactly, run npm install fabric@5 instead.
Examples of using fabric.js
Now that we know about Fabric.js, let’s see what wonders it brings to us in examples.
Example 1:
We can add simple shapes (objects) like a circle, triangles, and rectangles to our canvas.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Circle with Fabric.js</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">
</script>
<body>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;"></canvas>
<!-- Your script after Fabric.js is loaded -->
<script>
window.onload = function () {
// Create a fabric.js canvas
const canvas = new fabric.Canvas('myCanvas');
// Create a blue circle
const circle = new fabric.Circle({
radius: 50,
fill: 'blue',
left: 100,
top: 100
});
// Create a green triangle
const triangle = new fabric.Triangle({
width: 100,
height: 100,
fill: 'green',
left: 250,
top: 100
});
// Create a red rectangle
const rectangle = new fabric.Rect({
width: 150,
height: 75,
fill: 'red',
left: 100,
top: 250
});
// Add the shapes to the canvas
canvas.add(circle);
canvas.add(triangle);
canvas.add(rectangle);
};
</script>
</body>
</html>
Here, we create the fabric canvas and shape it. We also add customisations like width, height, colour fill, and position. Next, we add them to the canvas using the add method.
Output:

Example 2:
We can add gradient fill to our shapes. We can also set the mixing ratio and number of mixing colours according to our choice. Here is how we do it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Circle with Fabric.js</title>
<script src= "https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">
</script>
<body>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;"></canvas>
<!-- Your script after Fabric.js is loaded -->
<script>
window.onload = function() {
// Create a fabric.js canvas
const canvas = new fabric.Canvas('myCanvas');
// Gradient for the circle
const circleGradient = new fabric.Gradient({
type: 'linear',
gradientUnits: 'pixels',
coords: { x1: 0, y1: 0, x2: 100, y2: 0 }, // Adjust based on the circle's diameter
colorStops: [
{ offset: 0, color: 'blue' },
{ offset: 1, color: 'red' }
]
});
const circle = new fabric.Circle({
radius: 50,
fill: circleGradient,
left: 100,
top: 100
});
// Gradient for the triangle
const triangleGradient = new fabric.Gradient({
type: 'linear',
gradientUnits: 'pixels',
coords: { x1: 0, y1: 0, x2: 100, y2: 100 }, // Diagonal gradient
colorStops: [
{ offset: 0, color: 'green' },
{ offset: 0.33, color: 'yellow' },
{ offset: 0.78, color: 'green' },
{ offset: 1, color: 'yellow' }
]
});
const triangle = new fabric.Triangle({
width: 100,
height: 100,
fill: triangleGradient,
left: 250,
top: 100
});
// Gradient for the rectangle (rainbow)
const rectangleGradient = new fabric.Gradient({
type: 'linear',
gradientUnits: 'pixels',
coords: { x1: 0, y1: 0, x2: 150, y2: 0 }, // Horizontal gradient
colorStops: [
{ offset: 0, color: 'red' },
{ offset: 0.2, color: 'orange' },
{ offset: 0.4, color: 'yellow' },
{ offset: 0.6, color: 'green' },
{ offset: 0.8, color: 'blue' },
{ offset: 1, color: 'purple' }
]
});
const rectangle = new fabric.Rect({
width: 150,
height: 75,
fill: rectangleGradient,
left: 100,
top: 250
});
// Add the shapes to the canvas
canvas.add(circle);
canvas.add(triangle);
canvas.add(rectangle);
};
</script>
</body>
</html>
Here we have created different gradient objects for different shapes. (circleGradient, triangleGradient, rectangleGradient). In each gradient object, we have defined its type, coordinates, and offsets for colour mixing and proportion of mixing.
- Gradient for Circle: A linear gradient transitions from blue to red.
- Gradient for Triangle: A linear gradient with a mix of green and yellow.
- Gradient for Rectangle: A horizontal rainbow gradient transitioning through red, orange, yellow, green, blue, and purple.
Output:

Example 3:
We can add text into Canvas, not just normal text but text with many effects such as font size, font weight, text decoration, shadow, etc. Here is a code displaying ten text messages on the canvas with different effects:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Circle with Fabric.js</title>
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">
</script>
<body>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;"></canvas>
<!-- Your script after Fabric.js is loaded -->
<script>
window.onload = function() {
const canvas = new fabric.Canvas('myCanvas');
const text1 = new fabric.Text("I'm in Comic Sans", {
fontFamily: 'Comic Sans',
fill: 'purple',
left: 50,
top: 50
});
const text2 = new fabric.Text("I'm at fontSize 40", {
fontSize: 40,
fill: 'blue',
left: 50,
top: 100
});
const text3 = new fabric.Text("I'm at fontSize 20", {
fontSize: 20,
fill: 'green',
left: 50,
top: 160
});
const text4 = new fabric.Text("I'm normal weight", {
fontWeight: 'normal',
fill: 'red',
left: 50,
top: 200
});
const text5 = new fabric.Text("I'm bold weight", {
fontWeight: 'bold',
fill: 'orange',
left: 50,
top: 240
});
const text6 = new fabric.Text("I'm underlined", {
underline: true,
fill: 'pink',
left: 50,
top: 280
});
const text7 = new fabric.Text("I'm strikethrough", {
linethrough: true,
fill: 'brown',
left: 50,
top: 320
});
const text8 = new fabric.Text("I'm overlined", {
overline: true,
fill: 'teal',
left: 50,
top: 360
});
const text9 = new fabric.Text("I'm text with shadow", {
shadow: 'rgba(0,0,0,0.3) 5px 5px 5px',
fill: 'black',
left: 50,
top: 400
});
const text10 = new fabric.Text("And another shadow", {
shadow: 'rgba(0,0,0,0.2) 0 0 5px',
fill: 'cyan',
left: 50,
top: 440
});
// Add all text objects to the canvas
canvas.add(text1, text2, text3, text4, text5, text6, text7, text8, text9, text10);
};
</script>
</body>
</html>
In this example, we have used the following features:
- Font Family: It changes the text font to Comic Sans.
- Font Size: It helps to show two text objects with different font sizes (40 and 20).
- Font Weight: It helps to demonstrate normal and bold text.
- Text Decorations: Texts can be underlined, strikethrough, and overlined.
- Shadow: It helps to add shadow to the text
Output:

How the fabric.js object model works
Every shape inherits from fabric.Object, so Rect, Circle, Triangle and Text all share the same core properties and methods:
left,top,width,height,fill,stroke,angle,opacityget()andset()to read or change any property after creation
const rect = new fabric.Rect({ width: 100, height: 60, fill: 'teal', left: 50, top: 50 });
canvas.add(rect);
rect.set({ left: 200, top: 150, angle: 15 });
canvas.renderAll(); // required after set()
console.log(rect.get('width'));
- Always call
canvas.renderAll()afterset(). Fabric batches drawing, it does not auto-repaint. - No dragging or click handling needed? Use
fabric.StaticCanvasinstead offabric.Canvas. Same API, no event layer, faster for static previews or watermarks.
Exporting and saving canvas state
Two ways to persist a canvas:
Export as image. toDataURL returns a base64 string for direct download.
const dataURL = canvas.toDataURL({ format: 'png', quality: 1.0 });
const link = document.createElement('a');
link.href = dataURL;
link.download = 'canvas-export.png';
link.click();
Save editable state. toJSON and loadFromJSON keep every object, position and style intact for later editing.
const canvasState = canvas.toJSON();
canvas.loadFromJSON(canvasState, () => {
canvas.renderAll();
});
Snapshot toJSON() after every change to build undo/redo.
Building a real-time collaborative canvas
Pair fabric with Socket.IO to let multiple users draw on one canvas: emit local changes over the socket, apply remote changes as they arrive.
import { io } from 'socket.io-client';
const socket = io(window.location.origin);
canvas.on('object:added', (e) => {
if (e.target.fromSocket) return; // don't re-broadcast remote objects
socket.emit('object-added', e.target.toJSON());
});
socket.on('object-added', (objData) => {
fabric.util.enlivenObjects([objData], (objects) => {
objects[0].fromSocket = true;
canvas.add(objects[0]);
});
});
fromSocketflag stops an infinite broadcast loop- Debounce
object:modifiedduring drags, one drag can fire dozens of events a second
Fabric.js vs Konva.js: When to reach for it
The two actively maintained canvas libraries compared:
| Fabric.js | Konva.js | |
|---|---|---|
| Best for | Editors, SVG import/export | Games, drag-heavy dashboards |
| SVG support | Mature parser | Basic |
| API surface | Larger, object model | Leaner |
| Node.js support | Yes | Yes |
| Save/load | JSON built in | JSON built in |
Need SVG import and JSON save/load? Fabric fits. Need raw drag performance across hundreds of objects? Benchmark Konva too.
Fabric.js on Node.js
Since v6, fabric/node runs canvas rendering server-side using node-canvas and jsdom, no browser needed. Useful for thumbnails, watermarking or chart exports. Requires Node.js 18+.
import http from 'http';
import * as fabric from 'fabric/node';
http.createServer((req, res) => {
const canvas = new fabric.StaticCanvas(null, { width: 200, height: 200 });
const rect = new fabric.Rect({ width: 100, height: 60, fill: 'teal' });
canvas.add(rect);
canvas.renderAll();
res.setHeader('Content-Type', 'image/png');
canvas.createPNGStream().pipe(res);
}).listen(8080);
Key takeaways
- Fabric.js wraps native HTML5 canvas in an object model so shapes stay editable after creation
- Every shape inherits shared properties and methods from the base fabric.Object class
- Install via npm, yarn or a CDN script tag, current major release is v7
- v6 and later use named ESM imports instead of the older global fabric namespace
- toDataURL exports a flattened image while toJSON and loadFromJSON preserve editable state
- Pair fabric with Socket.IO to build a real-time collaborative canvas
- fabric/node runs canvas rendering on a server without a browser, Node 18 or higher required
- Konva.js is the main alternative, better suited to drag-heavy interactive graphics
Frequently asked questions
Is fabric.js still maintained in 2026?
Yes. Fabric.js is on major version 7 with regular releases and an active GitHub repository with tens of thousands of stars.
Can I use fabric.js with React or Next.js?
Yes. Wrap canvas creation in a useEffect hook, store the canvas instance in a ref and call canvas.dispose() on unmount to avoid memory leaks.
What is the difference between fabric.Canvas and fabric.StaticCanvas?
StaticCanvas has the full object model but skips mouse and touch event handling, so it renders faster for non-interactive graphics.
Does fabric.js work on the server with Node.js?
Yes. Import from fabric/node, which uses node-canvas and jsdom to render without a browser. Node.js 18 or higher is required.
How do I export a fabric.js canvas as an image?
Call canvas.toDataURL with a format option like png or jpeg, then use the returned base64 string as a download link’s href.
Is fabric.js better than Konva.js?
Neither wins outright. Fabric fits SVG-heavy editors better while Konva fits high-frequency drag and game-style interactions better.
How do I save and reload a fabric.js canvas later?
Use canvas.toJSON() to save the full object state and canvas.loadFromJSON() to restore it, followed by canvas.renderAll().


