Creating text
Text is an essential ingredient in a designer's toolkit, and apps can create and otherwise interact with text in a variety of ways. This page explains everything you need to know about working with text.
Text formats
While using the Apps SDK, text can be represented in either of the following formats:
- Plaintext
- Richtext
Apps can choose which format to work with, but the choice affects what features are available. Generally speaking, working with plaintext is simpler, while working with richtext is more flexible.
Plaintext
- When creating plaintext, formatting can only be applied to the whole text.
- When reading plaintext, apps can't access the existing formatting information.
- When updating plaintext, apps can't maintain the styling of the text.
Richtext
- When creating richtext, different parts of the text can have different formatting.
- When reading richtext, apps can access the existing formatting information.
- When updating richtext, apps can maintain the styling of the text.
Text elements
Apps can create text elements that contain text content. Users can then control the position, dimensions, and rotation of these containers.
To create a richtext element, an app must first create a richtext range. It's this range that defines the structure and formatting of the richtext. To learn more, see Richtext ranges.
import { addElementAtPoint, createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("Hello world");await addElementAtPoint({type: "richtext",range,});
import { addElementAtPoint } from "@canva/design";await addElementAtPoint({type: "text",children: ["Hello world"],});
Text content
When text exists in a design, it's known as text content. This content can exist in a variety of containers, including but not limited to:
- Table cells
- Text elements
It's important to understand that "content" is not synonymous with "element". This distinction matters because some of the features in the Apps SDK operate on content and not on elements (and vice-versa).
Text formatting
Apps can apply a variety of formatting options to text, including fonts.
In the case of richtext, there are two types of formatting:
- Paragraph formatting
- Inline formatting
Paragraph formatting options can only be applied to entire paragraphs, while inline formatting options can be applied to any range of text, including paragraphs. To learn more, see Formatting richtext.
In the case of plaintext, formatting can only be applied to the text as a whole. To view all of the available options, see the API reference.
Paragraphs
Apps can use the \n
character to denote the end of a paragraph, creating a line break in the resulting text:
import { createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("This is the first paragraph.\nThis is the second paragraph.");
import { addElementAtPoint } from "@canva/design";await addElementAtPoint({type: "text",children: ["This is the first paragraph.\nThis is the second paragraph."],});
Text selection
Apps can listen for the selection of text content in a user's design and then read or update the selected content. For example, an app could analyze and fix the grammar of the selected text content.
When richtext is selected, the text content is available as a richtext range.
import React from "react";import { Button } from "@canva/app-ui-kit";import { selection, SelectionEvent } from "@canva/design";import * as styles from "styles/components.css";export function App() {const [currentSelection, setCurrentSelection] = React.useState<SelectionEvent<"richtext"> | undefined>();const isElementSelected = (currentSelection?.count ?? 0) > 0;React.useEffect(() => {return selection.registerOnChange({scope: "richtext",onChange: setCurrentSelection,});}, []);async function handleClick() {if (!isElementSelected || !currentSelection) {return;}const draft = await currentSelection.read();for (const content of draft.contents) {// Get the text with formatting informationconst regions = content.readTextRegions();for (const region of regions) {// The plaintext content of a regionconsole.log(region.text);// The formatting information of a regionconsole.log(region.formatting);}}}return (<div className={styles.scrollContainer}><Buttonvariant="primary"disabled={!isElementSelected}onClick={handleClick}stretch>Read selected richtext content</Button></div>);}
When plaintext is selected, the text content is available as a plain string, without any formatting information.
import React from "react";import { Button } from "@canva/app-ui-kit";import { useSelection } from "utils/use_selection_hook"; // https://github.com/canva-sdks/canva-apps-sdk-starter-kit/blob/main/utils/use_selection_hook.tsimport * as styles from "styles/components.css";export function App() {const currentSelection = useSelection("plaintext");const isElementSelected = currentSelection.count > 0;async function handleClick() {if (!isElementSelected) {return;}const draft = await currentSelection.read();for (const content of draft.contents) {console.log(content.text);}}return (<div className={styles.scrollContainer}><Buttonvariant="primary"disabled={!isElementSelected}onClick={handleClick}>Read selected plaintext content</Button></div>);}
To learn more about selection, see:
Richtext ranges
A richtext range is an object that represents a portion of formatted text — anything from a single word to multiple paragraphs — and exposes methods for safely interacting with that range.
Before an app creates a richtext element, it must create a range:
import { createRichtextRange } from "@canva/design";const range = createRichtextRange();
By default, this range does not contain any text.
When an app reads existing richtext content, such as when using the Selection API, it receives a range from Canva:
const draft = await currentSelection.read();for (const content of draft.contents) {console.log(content); // This is the richtext range object}
The same methods are available to all ranges, whether or not they were created by the app.
There is no concept of a "plaintext range" because plaintext is a simple string that can be modified with standard string manipulation techniques.
Creating richtext
Apps can create richtext by appending it to a range:
import { createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("Hello world.");
This text can be formatted with inline formatting options:
import { createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("This is bold.", { fontWeight: "bold" });
If a formatting option isn't specified, its value is inherited from the text that precedes it:
import { createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("This is bold. ", { fontWeight: "bold" });range.appendText("This is also bold.");
In other words, when text is appended, formatting options stack on top of one another:
import { createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("This is bold. ", { fontWeight: "bold" });range.appendText("This is bold and pink.", { color: "#ff0099" });range.appendText("This is bold, pink, and italic.", { fontStyle: "italic" });
Formatting richtext
Apps can format text in a variety of ways.
A limited range of options are available when formatting inline text.
import { addElementAtPoint, createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("This is the first paragraph.\nThis is the second paragraph.");range.formatText({ start: 0, length: 4 }, { fontWeight: "bold" });
All formatting options are available when formatting paragraphs.
import { addElementAtPoint, createRichtextRange } from "@canva/design";const range = createRichtextRange();range.appendText("This is the first paragraph.\nThis is the second paragraph.");range.formatParagraph({ start: 0, length: 1 }, { textAlign: "center" });
Be aware that, when formatting paragraphs, any paragraph that overlaps with the specified bounds will be formatted — even if the paragraph only overlaps with a single character.
Reading richtext as plaintext
Apps can access the plaintext representation of richtext content:
import { createRichtextRange } from "@canva/design";// Create a richtext rangeconst range = createRichtextRange();range.appendText("Hello world");// Get the plaintext representation of the rangeconst plaintext = range.readPlaintext();console.log(plaintext); // => "Hello world"
A common use-case for this behavior is to check if a substring exists before processing the text further:
import { createRichtextRange } from "@canva/design";// Create a richtext rangeconst range = createRichtextRange();range.appendText("Hello world");// Get the plaintext representation of the rangeconst plaintext = range.readPlaintext();// Check if the plaintext contains a stringif (plaintext.includes("Hello")) {// TODO: Do something here}
Reading richtext as text regions
Apps can convert a richtext range into an array of text regions. This array allows the app to inspect how formatting is applied to text throughout the range.
import { createRichtextRange } from "@canva/design";// Create a richtext rangeconst range = createRichtextRange();range.appendText("Hello world");// Get an array of text regionsconst regions = range.readTextRegions();console.log(regions); // => [{ text: "Hello world" }]
Each region represents a distinctly formatted string of characters. For example, each of the following would be represented as a separate region:
- A string without any explicit formatting.
- A string with bold formatting.
- A string with bold and italic formatting.
This is demonstrated in the following code snippet:
import { createRichtextRange } from "@canva/design";// Create a richtext rangeconst range = createRichtextRange();range.appendText("First text region. ");range.appendText("Also the first text region. ");range.appendText("Second text region. ", { fontWeight: "bold" });range.appendText("Third text region.", { fontStyle: "italic" });// Get an array of text regionsconst regions = range.readTextRegions();console.log(regions.length); // => 3
Gotchas
- Apps can't set the line-height of text.
- Apps can't create text with a vertical writing mode.
- Text elements can't have a predefined height.
- Font sizes are defined with pixels (px) in the API but displayed as points (pt) in the UI.