# Konva vs Fabric.js for building a design editor

> Konva vs Fabric.js for a Canva clone in 2026: real npm numbers and side-by-side code for text, images, pages, export and server rendering.

- **Author**: Rishi Mohan
- **Published**: 2026-09-27
- **Tags**: Konva, Fabric.js, Canvas, Design Editor, Embed, Developer Tools
- **Read time**: 12 min read
- **URL**: https://orshot.com/blog/konva-vs-fabric-js

---

I build a design editor for a living, and developers building a Canva clone inside their product keep asking me the same question before they start: Konva or Fabric.js?

The pages that rank for it are either from 2022, vendor pages, or quote fps benchmarks nobody ran. So this post does two things. It gives you the real numbers, then walks through the editor features you'll actually build (text, images, pages, export, locking, server rendering) with the code for Konva, Fabric.js and [Orshot Embed](https://orshot.com/features/orshot-embed) side by side.

![Three ways to ship a design editor: Konva (React components, 55 KB, scene graph, 9.96M downloads a month), Fabric.js (inline text editing, SVG export, selection controls, 31.5K GitHub stars) and Orshot Embed (full editor UI, 2,000+ templates, render by API, multi-tenant, PDF and MP4 export)](https://orshot.com/blog/konva-vs-fabric-js/hero.webp)

**The short answer:** use Konva if you're building in React and drawing lots of shapes, and Fabric.js if your users edit text on the canvas or you need SVG export. Neither gives you undo/redo, pages, PDF export, per-user storage or server rendering, so budget for those either way.

**TL;DR** — Pick by what you ship:

- **Diagrams, whiteboards, games in React**: [Konva](#how-to-add-an-editable-text-layer) — Declarative react-konva components, a layered scene graph, 55 KB gzipped. Text editing is yours to build.
- **A text-heavy editor with SVG export**: [Fabric.js](#how-to-export-png-and-pdf) — Inline text editing and toSVG() are built in. The API is imperative, and two major versions broke code since 2024.
- **A Canva-like editor inside your SaaS**: [Orshot Embed](#or-skip-the-canvas-orshot-embed) — A finished, white-label editor with 2,000+ templates your users can fork, and every design renders by API.

## Konva vs Fabric.js by the numbers

|  |
| :-- |
| npm downloads / month |
| React bindings / month |
| GitHub stars |
| First released |
| Latest version |
| Size, min + gzip |
| Dependencies |
| Open / closed issues |
| Contributors |
| Official framework bindings |
| Inline text editing |
| SVG export |
| License |

Stars say Fabric. Downloads say Konva, almost three to one, with react-konva alone at 7.58M a month. Konva is smaller, has zero dependencies and [official React, Vue, Svelte and Angular bindings](https://konvajs.org/docs/faq.html "nofollow target=_blank"). Fabric is older and ships more editor behavior out of the box, mainly text editing and SVG export.

Konva's single open issue doesn't mean zero bugs: its maintainer closes issues fast. Numbers are from [npm](https://www.npmjs.com/package/konva "nofollow target=_blank"), [bundlephobia](https://bundlephobia.com/package/konva "nofollow target=_blank") and GitHub ([Konva](https://github.com/konvajs/konva "nofollow target=_blank"), [Fabric.js](https://github.com/fabricjs/fabric.js "nofollow target=_blank")), checked September 27, 2026.

## Or skip the canvas: Orshot Embed

Both libraries draw shapes. Neither is an editor. Konva's own maintainer [called them low-level](https://news.ycombinator.com/item?id=25431142 "nofollow target=_blank") when launching Polotno, the editor SDK built on top of Konva.

Orshot Embed is the editor itself, already built and hosted. You drop it into your app as an iframe or a React/Vue component, and your users design in it under your brand. When I built it, I skipped the canvas library entirely: every layer is a real HTML element, text is edited with a rich-text editor, and the server renders the same HTML and CSS in headless Chrome. So there's no textarea overlay, no blurry text on zoom, and PNG, PDF and MP4 come out of the same design.

|  |
| :-- |
| Inline text editing |
| Image replace, crop and masks |
| Multi-page designs |
| Undo / redo, snap guides |
| PNG, PDF and MP4 export |
| Per-user templates and permissions |
| Render the same design by API |
| Starter templates |
| SVG export |
| Your own engine, plugins, self-hosting |

> — Michael Ossendrijver, CEO, Incubeta

Your users start from any of our [2,000+ templates](https://orshot.com/templates), forked into your workspace, or from yours.

### Try it: a live Orshot Embed

"Acme Studio" is a made-up brand running a real embed. Switch templates, edit text, swap images; nothing is saved.

Embed from $39/mo. Multi-tenancy, webhooks and no Orshot branding from $160/mo.

[Get this editor in your app](https://orshot.com/pricing?via=blog-konva-fabric-demo)

Driving the editor from your own page (the `orshot:embed:control` messages below) needs the $349/mo plan. Already on Polotno? The [Polotno migration guide](https://orshot.com/migrate/from-polotno) maps each SDK call to its Orshot equivalent.

The rest of this post is the same editor feature built three ways.

## How to add an editable text layer

![Orshot Studio with a headline text layer selected: typography, fit-to-box and fill controls on the right, and the headline toggled as an API field](https://orshot.com/blog/konva-vs-fabric-js/studio-editor.webp)

Fabric ships `IText` and `Textbox`: double-click, a caret appears, selection and wrapping work on the canvas. Konva has no text editing. Its [docs](https://konvajs.org/docs/sandbox/Editable_Text.html "nofollow target=_blank") tell you to lay a `<textarea>` over the node and copy the value back on blur.

```js
const text = new Konva.Text({
  x: 60, y: 60, text: "Summer sale", fontSize: 40, draggable: true,
});
layer.add(text);

// No built-in editing: overlay a textarea on double-click
text.on("dblclick dbltap", () => {
  const pos = text.absolutePosition();
  const box = stage.container().getBoundingClientRect();
  const area = document.createElement("textarea");
  Object.assign(area.style, {
    position: "absolute",
    top: `${box.top + pos.y}px`,
    left: `${box.left + pos.x}px`,
    width: `${text.width()}px`,
    fontSize: `${text.fontSize()}px`,
  });
  area.value = text.text();
  document.body.appendChild(area);
  area.focus();
  area.addEventListener("blur", () => {
    text.text(area.value);
    area.remove();
  });
});
```

</CodeTab>

```js
import { Canvas, Textbox } from "fabric";

const canvas = new Canvas("editor");
const text = new Textbox("Summer sale", {
  left: 60, top: 60, width: 320, fontSize: 40,
  originX: "left", originY: "top", // v7 defaults to center
});
canvas.add(text);
canvas.setActiveObject(text);
```

</CodeTab>

```js
// Users edit text in the editor already. From your app,
// set the value of any text layer by its parameter name.
const iframe = document.querySelector("iframe");

iframe.contentWindow.postMessage(
  { type: "orshot:embed:control", modifications: { headline: "Summer sale" } },
  "*",
);
```

</CodeTab>
</CodeTabs>

Watch out for:
- **The Konva overlay is the happy path.** Konva's own page says it doesn't re-wrap while resizing, the caret goes stale when a web font loads late, and one style applies to the whole node.
- **Rich text is the long tail in Fabric too.** Its [curved-text request](https://github.com/fabricjs/fabric.js/issues/729 "nofollow target=_blank") has 119 comments and [text wrapping](https://github.com/fabricjs/fabric.js/issues/187 "nofollow target=_blank") 76.
- **Someone [sells a react-konva rich-text editor](https://dev.to/edward_hl_a93cc7f8b8077df/building-a-professional-react-konva-rich-text-editor-canvas-based-text-editing-done-right-20e8 "nofollow target=_blank") for $149.** That's the size of the job.

**With Orshot Embed:** mixed styles, fit-to-box, Google and custom fonts work out of the box, and any text layer can be toggled into an API field like the `headline` above. [Edit text in the live editor](#try-it).

## How to add an image layer

![Orshot Studio with a photo layer selected: Replace Image, crop, fit, clip shape and Remove BG controls, and the layer exposed as the hero_image API field](https://orshot.com/blog/konva-vs-fabric-js/image-layer.webp)

Both libraries load an image and give you drag handles. Replacing, cropping, masking and background removal are yours.

```js
Konva.Image.fromURL("https://example.com/photo.jpg", (img) => {
  img.setAttrs({ x: 80, y: 60, width: 400, height: 300, draggable: true });
  layer.add(img);
  layer.add(new Konva.Transformer({ nodes: [img] }));
});
```

</CodeTab>

```js
import { FabricImage } from "fabric";

const img = await FabricImage.fromURL("https://example.com/photo.jpg", {
  crossOrigin: "anonymous",
});
img.set({ left: 80, top: 60, originX: "left", originY: "top" });
img.scaleToWidth(400);
canvas.add(img);
canvas.setActiveObject(img);
```

</CodeTab>

```js
// Swap the photo in any image layer by its parameter name
iframe.contentWindow.postMessage(
  {
    type: "orshot:embed:control",
    modifications: { hero_image: "https://example.com/photo.jpg" },
  },
  "*",
);
```

</CodeTab>
</CodeTabs>

Watch out for:
- **Cross-origin images taint the canvas.** Without CORS headers and `crossOrigin: "anonymous"`, `toDataURL()` throws the moment a user exports.
- **Crop is a separate feature.** Both need a crop mode with its own handles; Fabric only added cropping controls, as an extension, in recent 7.x releases.

**With Orshot Embed:** users replace, crop and mask images from the image panel, and your app can swap photos in by URL. [Swap a photo in the live editor](#try-it).

## How to build multi-page designs

![Orshot Studio pages panel with four sizes of the same design as pages: square post, collage, landscape thumbnail and story](https://orshot.com/blog/konva-vs-fabric-js/pages-panel.webp)

Carousels, decks and "one design in four sizes" all need pages. Neither library has the concept, so you keep a list of page states and swap them in.

```js
const pages = [{ shapes: [] }, { shapes: [] }];
let current = 0;

function showPage(i) {
  current = i;
  layer.destroyChildren();
  pages[i].shapes.forEach((s) => layer.add(new Konva.Rect(s)));
}
```

</CodeTab>

```js
const pages = [canvas.toJSON()];
let current = 0;

async function showPage(i) {
  pages[current] = canvas.toJSON(); // save the page you're leaving
  current = i;
  await canvas.loadFromJSON(pages[i] ?? {});
  canvas.requestRenderAll();
}
```

</CodeTab>

```js
// Jump to page 2 and fill its layers
iframe.contentWindow.postMessage(
  { type: "orshot:embed:control", pageIndex: 2, modifications: { headline: "Tip 01" } },
  "*",
);

// Or fill any page without switching to it
iframe.contentWindow.postMessage(
  { type: "orshot:embed:control", modifications: { "page3@headline": "Tip 02" } },
  "*",
);
```

</CodeTab>
</CodeTabs>

**With Orshot Embed:** pages, page thumbnails, reordering and Smart Resize (one design re-laid out for every size) are built in, and a multi-page design exports as one PDF. [Flip through the carousel in the live editor](#try-it).

## How to load a saved design or template

![Orshot Embed preview for a test user: My Templates and a Templates Library of starting designs, including a carousel, an invoice, a roadmap and a pitch deck](https://orshot.com/blog/konva-vs-fabric-js/embed-templates.webp)

Saving is easy. Loading a design back months later, after a library upgrade, is where it bites. Konva's own docs [warn that `toJSON()` suits only very small apps](https://konvajs.org/docs/data_and_serialization/Best_Practices.html "nofollow target=_blank") and recommend keeping your own state.

```js
// Recommended: store your own document and rebuild nodes from it
const doc = await db.designs.get(designId);
layer.destroyChildren();
doc.shapes.forEach((s) => layer.add(new Konva.Rect(s)));

// Quick but fragile: Konva's own JSON
const stage = Konva.Node.create(doc.konvaJson, "container");
```

</CodeTab>

```js
const json = await db.designs.get(designId);

// JSON saved by v5 can render differently in v6/v7
// (renamed classes, center origin by default in v7)
await canvas.loadFromJSON(json);
canvas.requestRenderAll();
```

</CodeTab>

```js
// Open any template in the user's editor, pre-filled
iframe.contentWindow.postMessage(
  {
    type: "orshot:embed:control",
    templateId: "481",
    modifications: { headline: "Acme spring launch" },
  },
  "*",
);
```

</CodeTab>
</CodeTabs>

**With Orshot Embed:** designs are stored per user (pass a `userId`) and your server hears about every save through a webhook. Users start from your templates or any of the 2,000+ you fork from our library, and the originals stay protected. [Switch templates in the live editor](#try-it).

## How to export PNG and PDF

![Orshot Studio download panel: copy or save the current page as PNG, AVIF, PDF, HTML or MP4, or all four pages at once, at 2x scale](https://orshot.com/blog/konva-vs-fabric-js/download-panel.webp)

PNG at 2x works in both libraries. SVG is where they split: Fabric has `toSVG()`, Konva doesn't and [won't](https://news.ycombinator.com/item?id=43410988 "nofollow target=_blank"). PDF is DIY in both, and Fabric's ["how do I convert to PDF" issue](https://github.com/fabricjs/fabric.js/issues/5906 "nofollow target=_blank") has 69 comments.

```js
import { jsPDF } from "jspdf";

const png = stage.toDataURL({ pixelRatio: 2 });

// PDF: rasterize, then place the image yourself
const pdf = new jsPDF({ unit: "px", format: [stage.width(), stage.height()] });
pdf.addImage(png, "PNG", 0, 0, stage.width(), stage.height());
pdf.save("design.pdf");
```

</CodeTab>

```js
const png = canvas.toDataURL({ format: "png", multiplier: 2 });
const svg = canvas.toSVG();

// PDF: convert the SVG (svg2pdf.js) or rasterize with jsPDF
```

</CodeTab>

```js
// Ask the embed for the user's current design
iframe.contentWindow.postMessage(
  { type: "orshot:request:template", requestId: "export-1", format: "pdf" },
  "*",
);

window.addEventListener("message", (e) => {
  if (!e.origin.includes("orshot.com")) return;
  if (e.data.type === "orshot:template:content") upload(e.data.data.content);
});
```

</CodeTab>
</CodeTabs>

**With Orshot Embed:** users download PNG, AVIF, PDF, HTML and MP4 (one page or all), and every download fires an event your app can catch. No SVG, though: if your users need vector SVG, that's a point for Fabric. [Download a design from the live editor](#try-it).

## How to lock layers and user actions

Users will break templates if you let them. Both libraries can freeze an object on the canvas. Deciding who may delete, upload or add pages is app logic you write.

```js
logo.draggable(false);
logo.listening(false); // no clicks, no transformer

// Per-user rules (who can delete, upload...) are yours to enforce
```

</CodeTab>

```js
logo.set({
  selectable: false,
  evented: false,
  lockMovementX: true,
  lockMovementY: true,
});

// Per-user rules (who can delete, upload...) are yours to enforce
```

</CodeTab>

```js
import jwt from "jsonwebtoken";

// Sign who the user is and what they can't do
const token = jwt.sign(
  { sub: user.id, permissions: { "template/delete": true, "page/add": true } },
  process.env.ORSHOT_SIGNING_SECRET,
  { expiresIn: "15m" },
);

const src = `https://orshot.com/embeds/YOUR_EMBED_ID?token=${token}`;
```

</CodeTab>
</CodeTabs>

**With Orshot Embed:** locked layers, master templates that open as copies, and per-user permissions for saving, deleting, uploads, fonts, pages and downloads, set in the dashboard or per session in the token.

## How to render a design on a server

![Orshot Studio API tab for an image layer: hero_image toggled as a parameter, with example values for the image URL, border radius, border and alt text](https://orshot.com/blog/konva-vs-fabric-js/api-tab.webp)

The moment users design something, someone asks for bulk exports, scheduled posts or an API. Then the design has to render without a browser.

```js
// Konva 10: Node backends are opt-in now
import Konva from "konva";
import "konva/canvas-backend"; // npm i konva canvas

const stage = new Konva.Stage({ width: 1080, height: 1080 });
// rebuild layers from your stored document, then:
const png = stage.toDataURL({ pixelRatio: 1 });
```

</CodeTab>

```js
// Fabric 7, Node 20+
import { StaticCanvas, FabricText } from "fabric/node"; // npm i fabric canvas jsdom

const canvas = new StaticCanvas(null, { width: 1080, height: 1080 });
canvas.add(new FabricText("Summer sale", { left: 540, top: 540 }));
canvas.renderAll();
const png = canvas.toDataURL();
```

</CodeTab>

```bash
curl -X POST "https://api.orshot.com/v1/studio/render" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "TEMPLATE_ID",
    "modifications": {
      "headline": "Summer sale",
      "hero_image": "https://example.com/photo.jpg"
    },
    "response": { "type": "url", "format": "png" }
  }'
```

</CodeTab>
</CodeTabs>

Watch out for:
- **Konva 10 dropped built-in Node support.** Code that worked on v9 fails until you import `konva/canvas-backend` or `konva/skia-backend` ([setup docs](https://konvajs.org/docs/nodejs/nodejs-setup.html "nofollow target=_blank")).
- **Fonts must be registered with node-canvas separately**, or server output falls back to a default font.
- **You now ship native `canvas` binaries**, and Fabric in Node has a [history of memory leaks](https://github.com/fabricjs/fabric.js/issues/5102 "nofollow target=_blank") under load.

**With Orshot Embed:** the template a user saves is already an endpoint. Change text and images per request and get PNG, PDF or MP4 back from REST, n8n, Make or Zapier.

## How to use it in React

If you want a React canvas library, Konva wins: `react-konva` turns shapes into components. Fabric has no official bindings, so you keep its object tree in sync with your state by hand.

```jsx
import { Stage, Layer, Rect, Text } from "react-konva";

export function Editor({ items }) {
  return (
    <Stage width={1080} height={1080}>
      <Layer>
        {items.map((it) => (
          <Rect key={it.id} {...it} draggable />
        ))}
        <Text text="Summer sale" x={40} y={40} fontSize={48} />
      </Layer>
    </Stage>
  );
}
```

</CodeTab>

```jsx
import { useEffect, useRef } from "react";
import { Canvas, Rect } from "fabric";

export function Editor({ items }) {
  const el = useRef(null);
  const fc = useRef(null);

  useEffect(() => {
    fc.current = new Canvas(el.current, { width: 1080, height: 1080 });
    return () => fc.current.dispose();
  }, []);

  useEffect(() => {
    fc.current.clear();
    items.forEach((it) => fc.current.add(new Rect(it)));
  }, [items]);

  return <canvas ref={el} />;
}
```

</CodeTab>

```jsx
import { OrshotEmbed } from "@orshot/embed-react";

export function Editor({ user }) {
  return (
    <div style={{ height: 720 }}>
      <OrshotEmbed
        embedId="YOUR_EMBED_ID"
        templateId="TEMPLATE_ID"
        onTemplateUpdate={(t) => saveDesign(user.id, t)}
      />
    </div>
  );
}
```

</CodeTab>
</CodeTabs>

Watch out for:
- **react-konva 19.3 needs React 19.3 or newer.** On an earlier React 19, pin an older 19.x.
- **Both libraries break on server render in Next.js** (see react-konva's [SSR issue](https://github.com/konvajs/react-konva/issues/572 "nofollow target=_blank")). Load the canvas with `dynamic(() => import(...), { ssr: false })`.

**With Orshot Embed:** it's an iframe, so the same editor runs in React, Vue, Rails, PHP or WordPress, with no SSR edge cases.

## What building it costs

The "Watch out for" lists above are where editor projects run long. Toggle what your editor needs, set your costs, and compare building against buying over one to three years.

## Which one should you pick?

- **Konva** if you're drawing lots of interactive shapes in React and text is a label, not a document.
- **Fabric.js** if users edit text on the canvas and you need SVG for print, and you're fine pinning versions.
- **Orshot Embed** if the goal is "let our users make designs from templates, then render them." That's a product, not a library choice.

## Add Orshot Embed with your coding agent

Using Claude Code, Cursor or Codex? Paste this and it will wire the editor into your app, with per-user templates and a render endpoint.

**Q: Fabric.js vs Konva: which is faster?**

There's no public, reproducible benchmark. The 60fps vs 30fps figures that circulate have no method attached. Konva draws each layer on its own canvas, which helps when most shapes are static, and every shape listens for events by default, which hurts with thousands of them. Measure with your own scene.

**Q: Konva or Fabric.js for a Canva clone?**

Fabric gets you to a working text editor faster because inline editing and SVG export are built in. Konva fits React better. Either way you still build undo/redo, snapping, pages, templates and server rendering. If your users design inside your SaaS, Orshot Embed gives you that editor with 2,000+ templates and an API to render from.

**Q: What's the best React canvas library for a design editor?**

Konva, through react-konva, is the most used React canvas library: 7.58M downloads a month and official bindings, so shapes are components with props. Fabric.js works in React too but needs manual syncing. If you'd rather not build the editor at all, a design editor SDK like Orshot Embed drops in as a React component.

**Q: Does Konva support SVG export?**

No. Konva exports PNG or JPEG via toDataURL and toBlob, and its maintainer has said SVG export is out of scope. Fabric.js has canvas.toSVG().

**Q: Is upgrading Fabric.js to v6 or v7 a breaking change?**

Yes, both. v6 (June 2024) moved to TypeScript, named imports and promises and renamed classes, and its breaking-changes issue has 188 comments. v7 (December 2025) made objects default to a center origin, removed getPointer and requires Node 20+. Konva 10 (September 2025) went ESM-only and made Node backends opt-in.

**Q: Can I render Konva or Fabric designs on a server?**

Yes. Konva 10 needs an explicit backend import (konva/canvas-backend or konva/skia-backend) and Fabric uses fabric/node with node-canvas and jsdom. You register fonts separately and run native canvas binaries. Orshot renders saved templates through a REST API instead.

**Q: Is Polotno built on Konva?**

Yes. Polotno is built on Konva and react-konva by the same maintainer, and its package also depends on quill for rich text and mobx for state. Polotno is a commercial SDK from $249/mo, while Konva itself is MIT. If you're moving off Polotno, our migration guide at orshot.com/migrate/from-polotno maps each SDK call to Orshot Embed.

**Q: Can users start from templates in Orshot Embed?**

Yes. You can fork any of Orshot's 2,000+ templates into your workspace and they appear in your embed's template library. Users edit their own copy, and each saved template can be rendered by API with new data.

## The bottom line

Konva and Fabric are both good, MIT-licensed and maintained. Pick Konva for React and shapes, Fabric for text and SVG, then budget for every "Build it" in the table above, because that's where editor projects go long.

If what you really want is your users designing inside your app, skip the canvas and embed the editor.

2,000+ templates to fork, render any design by API.

[Try Orshot Embed](https://orshot.com/pricing?via=blog-konva-fabric-bottom)