Canva taught a very large number of people what a design tool should feel like. If your product has any design surface, whether that is book covers, t-shirt artwork, or social posts, that is the bar users measure you against.

The good news is that the hard part is not the editor. It is templates: giving people a starting point, then controlling exactly what they can change so the output stays on brand. This guide builds that in React with CreativeEditor SDK (CE.SDK).

If you would rather browse code than follow a tutorial, an older reference project is on GitHub at imgly/canva-clone-react-cesdk. It was built against an early CE.SDK release, so its calls differ from this guide, and it has had no commits since August 2024. The docs list it as a prebuilt Canva clone solution.

Updated September 2026.

What CE.SDK gives you

CreativeEditor SDK is an embeddable design editor that runs inside your own app, on your own domain. You mount it as a component, point it at your assets, and it handles the canvas, the layer model, text, images, and export.

The part that makes a Canva clone rather than a drawing tool is its role model. CE.SDK separates the person who builds a template from the person who fills it in. There are four roles in all, Creator, Adopter, Viewer and Presenter, and this guide uses the first two.

Creator mode

In Creator mode, you build the template. Add and arrange elements, apply filters and background removal, and then decide what the next person is allowed to touch.

Two features carry most of the weight. Placeholders mark an element as replaceable and control whether it can be deleted, restyled, or duplicated. Text variables let you define a token like {{Name}} and set it from code, which is how you batch-generate a hundred personalized cards from one design.

creator-mode-cesdk

Adopter mode

Adopter mode is what your end users get. They can change colors, text, and images, but only where the template’s creator allowed it. Everything else is locked, so a customer cannot accidentally drag the logo off the canvas or delete the legal line.

adopter-mode

That split is the whole trick. It is why a template-based editor produces usable output at scale and a blank canvas does not.

Build it

Prerequisites

  • Node.js 20.19+ or 22.12+
  • A React 18+ project on a modern build tool
  • A CE.SDK license key, optional to start and needed for production. Start a free trial to get one.

Create a project with Vite:

npm create vite@latest canva-clone -- --template react
cd canva-clone
npm install @cesdk/cesdk-js@1.82.0 @imgly/plugin-background-removal-web@1.82.0 onnxruntime-web@1.21.0

The last two packages run the background removal that the editor configuration in Step 1 imports.

Step 1: Mount the editor

CE.SDK ships a React component, so you do not manage the instance lifecycle yourself. Import it from the /react entry point and pass a config object and an init function.

The editor’s dock, inspector, and asset libraries come from an editor configuration, which the React design editor starter kit ships as source files. Copy them in:

npx degit imgly/starterkit-design-editor-ts-web/src/imgly#release-1.82.0 src/imgly

The release-1.82.0 tag matches the SDK version above. The configuration lives in your project from here, so you change the editor by editing those files.

// src/DesignEditor.jsx
import CreativeEditor from '@cesdk/cesdk-js/react';
import { initDesignEditor } from './imgly';

const config = {
  userId: 'YOUR_USER_ID',
};

const init = async (cesdk) => {
  // Editor UI, asset libraries, upload sources and background removal
  await initDesignEditor(cesdk);

  await cesdk.actions.run('scene.create');
};

export default function DesignEditor() {
  return (
    <CreativeEditor config={config} init={init} width="100vw" height="100vh" />
  );
}

You do not need a CE.SDK license key to try it: without one the editor runs in evaluation mode and exports carry a watermark. Start a free trial to get one and set it as license in the config.

That already gives you a working design editor with stock assets, fonts, shapes, and upload. Render it from App.jsx and run npm run dev.

The editor loads its engine assets from the IMG.LY CDN, which is meant for development. For production, serve the assets from your own servers.

Vite’s starter styles give #root a fixed width, which cuts the editor off on wide screens. Delete the #root { ... } rule in src/index.css and keep the rest of the file.

Name your own component something other than CreativeEditor, or it collides with the import.

Step 2: Add a template library

Templates are the difference between a design tool and a Canva clone. You register a template source, add assets to it, and tell CE.SDK what to do when a user picks one. This init replaces the one from Step 1.

// src/DesignEditor.jsx, replaces the init from Step 1
const init = async (cesdk) => {
  const engine = cesdk.engine;

  await initDesignEditor(cesdk);
  await cesdk.actions.run('scene.create');

  // Register a source and define what happens on click
  engine.asset.addLocalSource('my.templates', undefined, async (asset) => {
    const uri = asset.meta?.uri;
    const scene = engine.scene.get();
    if (!uri || scene == null) return undefined;

    await engine.scene.applyTemplateFromURL(
      new URL(uri, window.location.href).href
    );
    return scene;
  });

  // Show the source in the Templates panel
  cesdk.ui.updateAssetLibraryEntry('ly.img.templates', {
    sourceIds: ({ currentIds }) => [...currentIds, 'my.templates'],
  });

  // Labels for the panel section and the confirmation dialog
  cesdk.i18n.setTranslations({
    en: {
      'libraries.my.templates.label': 'My templates',
      'libraries.ly.img.templates.confirmation.headline': 'Apply template?',
      'libraries.ly.img.templates.confirmation.body':
        'This replaces the current design.',
      'libraries.ly.img.templates.confirmation.confirm': 'Apply',
      'libraries.ly.img.templates.confirmation.abort': 'Cancel',
    },
  });

  // Add templates to it
  engine.asset.addAssetToSource('my.templates', {
    id: 'postcard-1',
    label: { en: 'Postcard' },
    tags: { en: ['postcard', 'card'] },
    groups: ['cards'],
    meta: {
      thumbUri:
        'https://cdn.img.ly/packages/imgly/cesdk-js/1.82.0/assets/ly.img.templates/thumbnails/cesdk_postcard_1.jpg',
      uri: 'https://cdn.img.ly/packages/imgly/cesdk-js/1.82.0/assets/ly.img.templates/templates/cesdk_postcard_1.scene',
    },
  });
};

The starter kit’s Templates panel lists only its own sources, so updateAssetLibraryEntry adds yours to that list. The panel asks users to confirm before it applies a template, and setTranslations gives that dialog your wording and names your section in the panel. The confirmation keys name ly.img.templates rather than your own source id, so the kit’s templates from that source pick up your wording too.

applyTemplateFromURL is the important call. It replaces the content of the current page with the template’s content, but keeps the scene’s page size and design unit, so the template fits the format the user already picked.

In production, addAssetToSource is where your own designs go. A template is a .scene file, so the loop is: build it in Creator mode, save it, host it, and register it here with a thumbnail.

If you are generating designs from data rather than letting users pick, engine.scene.loadFromURL() plus engine.variable.setString() plus engine.block.export() is the batch path. The template library docs cover both directions.

Step 3: Export

Run this from a handler of your own, such as a download button, where engine is the cesdk.engine your init function received.

const page = engine.scene.getCurrentPage();
const blob = await engine.block.export(page, { mimeType: 'image/png' });

const anchor = document.createElement('a');
anchor.href = URL.createObjectURL(blob);
anchor.download = 'design.png';
anchor.click();

PNG covers most on-screen use. If people are ordering physical prints, export a PDF with mimeType: 'application/pdf' instead. CE.SDK exports PDFs in RGB, so CMYK output with ICC profiles needs the Print Ready PDF plugin. Bleed comes from the page margins, and the export for printing guide covers the rest.

Upgrading an older integration

If your integration was written against an older CE.SDK release, these are the API changes that affect this setup. CreativeEditorSDK.init() became create(), which does not build a scene for you and instead lets you configure the SDK first. The presets configuration, including presets.templates, gave way to the Asset API shown above. addDefaultAssetSources() and addDemoAssetSources() are deprecated in favor of the editor configuration above, and createDesignScene() in favor of actions.run('scene.create'), which needs no scene mode because scene mode was deprecated in 1.72. The deprecated calls still run. The docs carry per-version upgrade guides with the details for each release.

Let an agent build it

If you work in an AI-assisted editor, Agent Skills for CE.SDK load the CE.SDK documentation into Claude Code and similar tools, so you get code written against today’s API rather than the version that happened to be in the training data. There is also an MCP server for live lookup. Background in Introducing IMG.LY Agent Skills.

Where to take it

The obvious next steps are your own templates and your own assets. After that:

  • Custom UI. Reorder or replace editor components with setComponentOrder({ in: location }, order), and register your own buttons and panels. See the UI extensions guide.
  • Theming. Match your product with cesdk.ui.setTheme() or see the theming guide.
  • Brand assets. Serve fonts, logos, and imagery from your own backend so users only see approved material.
  • Automation. Text variables plus the engine API generate personalized designs at volume. We built an NFT art collection generator on exactly that.

If you are coming from Canva’s own API rather than starting fresh, two follow-ups go deeper: why teams move to a white-label alternative to Canva Connect, and a step-by-step guide to migrating from Canva Connect to the IMG.LY SDK.

Questions about your use case? Talk to us.

Frequently Asked Questions

Do I need a license key to try the editor? Not to try it. Without a license key the editor runs in evaluation mode and every exported design carries an IMG.LY watermark. A license key removes the watermark.

Which packages does the starter kit configuration need? @cesdk/cesdk-js, @imgly/plugin-background-removal-web and onnxruntime-web. The configuration imports the background removal plugin, so the build fails to resolve that import if the package is missing.

Why doesn’t my template source show up in the Templates panel? The starter kit’s Templates panel lists only its own sources. Add yours with cesdk.ui.updateAssetLibraryEntry('ly.img.templates', { sourceIds: ({ currentIds }) => [...currentIds, 'my.templates'] }), as in Step 2.

Does applying a template keep what the user already added? No. applyTemplateFromURL replaces the content of the page with the template’s content. It keeps the current page size and design unit and fits the template to them.

Why is the editor cut off on a wide screen? The Vite React template gives #root a fixed width in src/index.css. Delete the #root { ... } rule and the editor fills the window.

Can I export CMYK PDFs for print? CE.SDK exports PDFs in RGB. For CMYK output with ICC profiles, add the Print Ready PDF plugin, which converts the export and embeds the profile.