Step by step on how to setup fabric.js in the next.js app
Tags: webdev, javascript, react, nextjs
Published: 2023-09-30 03:12:44

## 🧐 What is `fabric.js`

[Fabric.js](http://fabricjs.com/) is a powerful and simple
Javascript HTML5 canvas library. It provides an interactive object model on top of the canvas element.
Fabric also has SVG-to-canvas (and canvas-to-SVG) parser.


![Fabric.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j7tkttpir9ziyga70w7x.png)

## What we build in this blog


![End Product](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jo2w123gcfzvjn7fqz5.gif)

The demo above is taken from [this medium blog](https://aprilescobar.medium.com/part-2-fabric-js-on-react-fabric-rect-533c5d8bbe55), in which there's some slight modification or fix need to be done.

---

## Install `fabric.js`

#### npm

```bash
npm i fabric
npm i -D @types/fabric # typescript
```

#### yarn

```bash
yarn add fabric
yarn add -D @types/fabric # typescript
```

#### pnpm

```bash
pnpm add fabric
pnpm add -D @types/fabric # typescript
```

---

## Setup for next.config.js

Below is the code for next.config.js. The reason is to configure the webpack for canvas runtime error that will crash the application and utf-8-validate & bufferutil warning. View more at the relevant references.

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  // reactStrictMode: false,
  webpack: (config) => {
    config.externals.push({
      "utf-8-validate": "commonjs utf-8-validate",
      bufferutil: "commonjs bufferutil",
      canvas: "commonjs canvas",
    });
    // config.infrastructureLogging = { debug: /PackFileCache/ };
    return config;
  },
};

module.exports = nextConfig;
```

### See also relevant references

1. [Appropriate webpack loader for canvas](https://www.reddit.com/r/nextjs/comments/13sbk05/what_is_the_appropriate_webpack_loader_for_the/)
2. [Common js - utf-8-validate & bufferutil](https://github.com/vercel/next.js/issues/44273#issuecomment-1374989371)

---

## Boilerplate for Next.js with Fabric.js

Below is the `next.js` `.tsx` code. If you are using Next.js 13.4 and above and opt-in to the app router, add `"use client"` on top of the file to use the Next.js client component, or else ignore the line.

```typescript
"use client"; // next.js app router

import React, { useState, useEffect } from "react";
import { fabric } from "fabric";

const App = () => {
  const [canvas, setCanvas] = useState<fabric.Canvas>();

  useEffect(() => {
    const c = new fabric.Canvas("canvas", {
      // ...
    });

    // settings for all canvas in the app
    // ...

    setCanvas(c);

    return () => {
      // ...
    };
  }, []);

  const addRect = (canvi?: fabric.Canvas) => {
    // ...
  };

  return (
    <div>
      <button onClick={() => addRect(canvas)}>Rectangle</button>
      <canvas id="canvas" />
    </div>
  );
};

export default App;
```

Ensure that `<canvas>` tag has an ID the same as `new fabric.Canvas(id, {...})`

## useEffect & addRect function

```typescript
  useEffect(() => {
    const c = new fabric.Canvas("canvas", {
      height: 400,
      width: 800,
      backgroundColor: "black",
    });

    // settings for all canvas in the app
    fabric.Object.prototype.transparentCorners = false;
    fabric.Object.prototype.cornerColor = "#2BEBC8";
    fabric.Object.prototype.cornerStyle = "rect";
    fabric.Object.prototype.cornerStrokeColor = "#2BEBC8";
    fabric.Object.prototype.cornerSize = 6;

    setCanvas(c);

    return () => {
      c.dispose();
    };
  }, []);

  const addRect = (canvas?: fabric.Canvas) => {
    const rect = new fabric.Rect({
      height: 280,
      width: 200,
      stroke: "#2BEBC8",
    });
    canvas?.add(rect);
    canvas?.requestRenderAll();
  };
```

### Note

`c.dispose()` is required to clean up the instantiated canvas element. See why you should clean up so that your rectangle can move and resize properly -> [StackOverflow Question](https://stackoverflow.com/questions/76885346/re-rendering-with-react-strict-mode-and-fabrics-canvas-instantiation).

If you refuse to add the `return () => {c.dispose();};` statement, you can disable the `reactStrictMode` in `next.config.js`. See the concept of reactStrictMode in this [StackOverflow Question](https://stackoverflow.com/questions/72238175/why-useeffect-running-twice-and-how-to-handle-it-well-in-react).

---

❤️ Leave a like if you like this post
❤️ It will be a massive motivation for me