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:

Without Fabric.js

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:

With Fabric.js

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.

&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">&lt;/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.

&lt;!DOCTYPE html>
&lt;html lang="en">

&lt;head>
    &lt;meta charset="UTF-8">
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0">
    &lt;title>Circle with Fabric.js&lt;/title>
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">
    &lt;/script>

&lt;body>
    &lt;canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;">&lt;/canvas>

    &lt;!-- Your script after Fabric.js is loaded -->
    &lt;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);
        };
    &lt;/script>
&lt;/body>

&lt;/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 1

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:

&lt;!DOCTYPE html>
&lt;html lang="en">
&lt;head>
    &lt;meta charset="UTF-8">
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0">
    &lt;title>Circle with Fabric.js&lt;/title>
    &lt;script src= "https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">
    &lt;/script>
    &lt;body>
    &lt;canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;">&lt;/canvas>

    &lt;!-- Your script after Fabric.js is loaded -->
    &lt;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);
      };
    &lt;/script>
&lt;/body>
&lt;/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 2

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:

&lt;!DOCTYPE html>
&lt;html lang="en">
&lt;head>
    &lt;meta charset="UTF-8">
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0">
    &lt;title>Circle with Fabric.js&lt;/title>
    &lt;script src=
"https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js">
    &lt;/script>
    &lt;body>
    &lt;canvas id="myCanvas" width="500" height="500" style="border:1px solid #000;">&lt;/canvas>

    &lt;!-- Your script after Fabric.js is loaded -->
    &lt;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);
        };
    &lt;/script>
&lt;/body>
&lt;/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:

Example 3

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, opacity
  • get() and set() 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() after set(). Fabric batches drawing, it does not auto-repaint.
  • No dragging or click handling needed? Use fabric.StaticCanvas instead of fabric.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]);
  });
});
  • fromSocket flag stops an infinite broadcast loop
  • Debounce object:modified during 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.jsKonva.js
Best forEditors, SVG import/exportGames, drag-heavy dashboards
SVG supportMature parserBasic
API surfaceLarger, object modelLeaner
Node.js supportYesYes
Save/loadJSON built inJSON 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().

Reference

http://fabricjs.com/

Aditya Gupta
Aditya Gupta
Articles: 512