Creating shapes

How to add shapes to a user's design.

Shapes are fundamental elements that users can add to their Canva designs. There are a number of shapes built into Canva, such as circles and rectangles, and apps can create their own shapes.

  • Shapes can be resized without becoming blurry.
  • Shapes are faster to create than equivalent images.
  • Shapes don't count against a user's upload quota.

After an app adds a shape to a user's design, the shape is indistinguishable from Canva's built-in shapes. This means the user can manipulate the shape in all the same ways, including:

  • Moving, resizing, and rotating the shape
  • Adjusting the color(s) of the shapes

Shapes are comparable to Scalable Vector Graphics (SVGs), in that they're made up of paths, fills, strokes, and a view box. There are, however, some nuances that distinguish them from SVGs.

A path is a combination of one or more lines that forms a shape. For example, a path could be a single line that forms a circle or a combination of lines that forms a hexagon.

By themselves, paths are not visible. They define the boundaries of a shape but not the contents of a shape. For a path to be made visible, it requires a fill or a stroke.

A path command defines the position, length, and curvature of a path.

Path commands are made up of two ingredients:

  • A letter that defines what the path command does
  • A list of numeric parameters

For example, the following path commands draw a square:

M 0 0
H 100
V 100
H 0
L 0 0

Here's a breakdown of what these commands are doing:

  • M 0 0 moves the current point to 0 on the X axis and 0 on the Y axis.
  • H 100 draws a line to 100 on the X axis and 0 on the Y axis.
  • V 100 draws a line to 100 on the X axis and 100 on the Y axis.
  • H 0 draws a line to 0 on the X axis and 100 on the Y axis.
  • L 0 0 draws a line to 0 on the X axis and 0 on the Y axis.

When creating shapes, the following path commands are available:

  • MoveTo: M, m
  • LineTo: L, l, H, h, V, v
  • Cubic Bézier Curve: C, c, S, s
  • Elliptical Arc Curve: A, a
  • ClosePath: Z, z

A command that is available to SVGs but is not available to shapes is the Q command, which is used to create quadratic Bézier curves.

Path commands are case sensitive and their behavior depends on their casing.

When a command is uppercase, the parameters are absolute coordinates — that is, relative to an origin of 0,0. When a command is lowercase, the parameters are relative to the previous coordinate.

  • A shape must have between 1 and 30 paths.
  • The maximum combined size of all paths must not exceed 2kb.
  • Paths must start with an M command.
  • Paths must not have more than one M command.
  • Paths must not use the Q command.
  • Paths must be closed in either of the following ways:
    • With a Z command at the end
    • By having the last coordinate match the first coordinate

A fill defines the interior of a path. It can be a color, an image, or a video. For example, if you wanted to create a pink square, the color pink would be the fill.

It's also worth noting that:

  • The maximum number of unique fill colors across a shape's paths must not exceed 6.
  • If a fill is a drop target and doesn't have a color, image, or a video, it will be a frame.
  • If a fill is not a drop target and doesn't have a color, image, or a video, it will be transparent.

A stroke is an outline that can be applied to a path. This outline is rendered along the edges of a path and can be configured with a custom weight (width) and color.

The view box is a rectangular area that defines the coordinates of the shape. It determines how the shape should be scaled, rotated, or positioned when it's displayed.

To learn more, see the MDN documentation.

In the Developer Portal, enable the canva:design:content:write permission. In the future, the Apps SDK will throw an error if the required permissions are not enabled. To learn more, see Configuring permissions.

Import the addElementAtPoint method (or addElementAtCursor, depending on the current context) from the @canva/design package:

import { addElementAtPoint } from "@canva/design";
ts

Call the method, passing in the structure of a shape as the only argument. The following code sample demonstrates how to create a pink square with the minimum required properties:

await addElementAtPoint({
type: "shape",
paths: [
{
d: "M 0 0 H 100 V 100 H 0 L 0 0",
fill: {
color: "#ff0099",
},
},
],
viewBox: {
height: 100,
width: 100,
left: 0,
top: 0,
},
});
ts

You can create more complex shapes by adding additional, more complex paths.

The fill of a shape can be defined as a drop target. When a fill is a drop target, users can update a fill by dragging and dropping an image or video onto it.

To define a fill as a drop target:

  1. Import the addElementAtPoint method (or addElementAtCursor, depending on the current context) from the @canva/design package:

    import { addElementAtPoint } from "@canva/design";
    ts
  2. When creating a fill, set the dropTarget property to true:

    await addElementAtPoint({
    type: "shape",
    paths: [
    {
    d: "M 0 0 H 100 V 100 H 0 L 0 0",
    fill: {
    dropTarget: true,
    color: "#ff0099",
    },
    },
    ],
    viewBox: {
    height: 100,
    width: 100,
    left: 0,
    top: 0,
    },
    });
    ts

When importing addElementAtPoint from @canva/design, be aware that:

  • The dropTarget property is optional for all fills, but we encourage you to include it and set to false in case of any future API changes.

The fill of a shape can be a color, image, or video.

To set the fill to an image or video:

  1. Import the addElementAtPoint method (or addElementAtCursor, depending on the current context) from the @canva/design package:

    import { addElementAtPoint } from "@canva/design";
    ts
  2. Import the upload method from the @canva/asset package:

    import { upload } from "@canva/asset";
    ts
  3. Upload an image or video to Canva's backend:

    const image = await upload({
    type: "image",
    mimeType: "image/jpeg",
    url: "https://www.canva.dev/example-assets/image-import/image.jpg",
    thumbnailUrl:
    "https://www.canva.dev/example-assets/image-import/thumbnail.jpg",
    });
    ts

    The upload method returns a ref property. This property contains a unique identifier that points to an asset in Canva's backend. To learn more, see Uploading assets.

  4. Pass the ref property into the fill:

    await addElementAtPoint({
    type: "shape",
    paths: [
    {
    d: "M 0 0 H 100 V 100 H 0 L 0 0",
    fill: {
    dropTarget: false,
    asset: {
    type: "image",
    ref: image.ref,
    },
    },
    },
    ],
    viewBox: {
    height: 100,
    width: 100,
    left: 0,
    top: 0,
    },
    });
    ts
import React from "react";
import { addElementAtPoint } from "@canva/design";
export function App() {
async function handleClick() {
// Add the shape to the design
await addElementAtPoint({
type: "shape",
paths: [
{
d: "M 0 0 H 100 V 100 H 0 L 0 0",
fill: {
color: "#ff0099",
},
},
],
viewBox: {
height: 100,
width: 100,
left: 0,
top: 0,
},
});
}
return (
<div>
<button onClick={handleClick}>Add shape to design</button>
</div>
);
}
tsx