Set up a Development URL (with TypeScript)
Learn how to set up a Development URL with webpack and TypeScript.
In the Developer Portal, some extensions have an Extension source field. This field determines where Canva loads the extension from.
The field has two options:
  • JavaScript file
  • Development URL
By providing a Development URL, you can make changes to an extension and see the results of those changes by simply closing and reopening the extension in the editor.
This tutorial explains how to set up a Development URL with TypeScript and webpack.
If you're developing an extension with JavaScript instead of TypeScript, see Set up a Development URL (with JavaScript).

Step 1: Create a project

To begin, set up a project with the following structure:
  • my-app/
    • dist/
    • src/
      • index.html
      • index.ts
    • package.json
    • tsconfig.json
    • webpack.config.ts
To set up this structure, run the following commands:
1
mkdir my-app my-app/src my-app/dist
2
cd my-app
3
npm init --yes # yarn init --yes
4
touch src/index.html src/index.ts webpack.config.ts
5
tsc --init
Copied!
The touch command is not available on Windows. If you're using Windows, create the files manually.
Then copy the following code into the src/index.html file:
1
<!DOCTYPE html>
2
<html style="max-width: 100%; max-height: 100%; overflow: hidden">
3
<body style="max-width: 100%; max-height: 100%; margin: 0">
4
<script>
5
// Get the URL and version number of JavaScript client
6
const params = new URLSearchParams(window.location.search);
7
const baseUrl = params.get("libBase");
8
const version = params.get("lib");
9
10
// Construct the URL of the JavaScript client
11
const libUrl = baseUrl + version + ".js";
12
13
// Load the JavaScript client
14
const lib = document.createElement("script");
15
lib.src = libUrl;
16
lib.onload = () => {
17
// Load the JavaScript bundle for the extension
18
const extension = document.createElement("script");
19
extension.src = "main.js";
20
document.body.appendChild(extension);
21
};
22
23
document.head.appendChild(lib);
24
</script>
25
</body>
26
</html>
Copied!
When Canva loads an extension via the Development URL, this code loads the JavaScript client that lets the extension interact with the Canva editor.
While you're developing an extension with a Development URL, your extension can send arbitrary HTTP requests to non-Canva domains. When you upload a JavaScript file though, those requests will fail.

Step 2: Install the dependencies

In the project's directory, install the following dependencies:
To install these dependencies, run the following command:
1
npm install @canva/editing-extensions-api-typings @types/webpack @types/webpack-dev-server ts-loader ts-node [email protected] webpack webpack-cli webpack-dev-server --save-dev
2
# yarn add @canva/editing-extensions-api-typings @types/webpack @types/webpack-dev-server ts-loader ts-node [email protected] webpack webpack-cli webpack-dev-server --dev
Copied!
These commands install the latest versions of these dependencies, which may have breaking changes that cause issues when setting up a Development URL. Refer to the package.json file for versions that are confirmed to work.

(Optional) Step 3: Configure TypeScript

If you're planning on rendering rich controls with JSX, set the jsx option in the tsconfig.json file to "react":
1
{
2
"compilerOptions": {
3
"jsx": "react"
4
}
5
}
Copied!
You also need to install the @types/react package:
1
npm install @types/react --save-dev
2
# yarn add @types/react --dev
Copied!

Step 4: Configure webpack

Copy the following code into the webpack.config.ts file:
1
import * as path from "path";
2
import { Configuration as WebpackConfiguration } from "webpack";
3
import { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server";
4
5
interface Configuration extends WebpackConfiguration {
6
devServer?: WebpackDevServerConfiguration;
7
}
8
9
const DIST_DIR = path.resolve(__dirname, "dist");
10
const SRC_DIR = path.resolve(__dirname, "src");
11
12
const config: Configuration = {
13
module: {
14
rules: [
15
{
16
test: /\.tsx?$/,
17
use: [
18
{
19
loader: "ts-loader",
20
options: {
21
allowTsInNodeModules: true,
22
},
23
},
24
],
25
},
26
],
27
},
28
resolve: {
29
extensions: [".js", ".json", ".jsx", ".ts", ".tsx"],
30
},
31
devServer: {
32
contentBase: [DIST_DIR, SRC_DIR],
33
https: true,
34
port: 9090,
35
},
36
};
37
38
export default config;
Copied!
This configuration:
  • Uses TypeScript instead of JavaScript (although JavaScript would also work).
  • Enables the allowTsInNodeModules option for ts-loader.
  • Resolves files with TypeScript and JavaScript extensions.
  • Runs the local development server over HTTPS.
  • Serves content from the dist and src directory.
If you plan on changing the webpack or TypeScript configuration, make the changes incrementally and test that the build still compiles after each change.

Step 5: Create "build" and "start" scripts

Add the following scripts to the package.json file:
1
"scripts": {
2
"build": "webpack --config ./webpack.config.ts --mode production",
3
"start": "webpack serve --config ./webpack.config.ts --mode development"
4
}
Copied!
To build a JavaScript bundle, run the following command:
1
# Build a JavaScript bundle
2
npm run build # yarn build
Copied!
To start a local development server, run the following command:
1
# Start a local development server
2
npm run start # yarn start
Copied!
By default, webpack builds a main.js file to the dist directory.

Step 6: Handle the invalid security certificate

If you start the local development server and navigate to https://localhost:9090, the following warning appears:
NET::ERR_CERT_AUTHORITY_INVALID
This is because the server doesn't have a valid security certificate.
Because of this warning, Canva won't load the extension.
There's a few ways to solve this problem:

Option 1: Set up a local security certificate

You can create a local, self-signed security certificate. This is the best option, since once it's setup, you don't have to think about it again.
To learn how to set up a certificate, see Set up a local security certificate.

Option 2: Bypass the warning

You know the server is secure, since you're the one who set it up, so it's okay to bypass the warning.
To bypass the warning in Google Chrome:
  1. 1.
    Start the development server.
  2. 2.
  3. 3.
    Click Advanced.
  4. 4.
    Click Proceed to localhost (unsafe).
A blank page should appear.
You need to bypass this warning every time you start the server.

Option 3: Use ngrok

You can use ngrok to expose a local server via a public, HTTPS-enabled URL. This provides a valid security certificate and prevents the warning from occurring.

Step 7: Preview the extension

To verify that the Development URL is working as expected:
  1. 1.
    Create an extension via the Developer Portal.
  2. 2.
    Add some code to the src/index.ts file.
  3. 3.
    In the Development URL field, enter the URL of the server.
  4. 4.
    Click Preview.
  5. 5.
    Select the extension.
  6. 6.
    When the Canva editor loads, click Use.
If the extension doesn't load, check the JavaScript Console for errors.

Example

src/index.html

1
<!DOCTYPE html>
2
<html style="max-width: 100%; max-height: 100%; overflow: hidden">
3
<body style="max-width: 100%; max-height: 100%; margin: 0">
4
<script>
5
// Get the URL and version number of JavaScript client
6
const params = new URLSearchParams(window.location.search);
7
const baseUrl = params.get("libBase");
8
const version = params.get("lib");
9
10
// Construct the URL of the JavaScript client
11
const libUrl = baseUrl + version + ".js";
12
13
// Load the JavaScript client
14
const lib = document.createElement("script");
15
lib.src = libUrl;
16
lib.onload = () => {
17
// Load the JavaScript bundle for the extension
18
const extension = document.createElement("script");
19
extension.src = "main.js";
20
document.body.appendChild(extension);
21
};
22
23
document.head.appendChild(lib);
24
</script>
25
</body>
26
</html>
Copied!

src/index.ts

1
import { EditingExtensionClient } from "@canva/editing-extensions-api-typings";
2
3
const canva: EditingExtensionClient.API = window.canva.init();
4
5
canva.onReady(async (opts) => {
6
console.log(opts);
7
});
Copied!

package.json

1
{
2
"name": "my-app",
3
"version": "1.0.0",
4
"scripts": {
5
"build": "webpack --config ./webpack.config.ts --mode production",
6
"start": "webpack serve --config ./webpack.config.ts --mode development"
7
},
8
"devDependencies": {
9
"@canva/editing-extensions-api-typings": "*",
10
"@types/react": "^17.0.15",
11
"@types/webpack": "^5.28.0",
12
"@types/webpack-dev-server": "^3.11.5",
13
"ts-loader": "^9.2.4",
14
"ts-node": "^10.1.0",
15
"typescript": "^4.3.5",
16
"webpack": "^5.46.0",
17
"webpack-cli": "^4.7.2",
18
"webpack-dev-server": "^3.11.2"
19
}
20
}
Copied!

tsconfig.json

1
{
2
"compilerOptions": {
3
"jsx": "react"
4
}
5
}
Copied!

webpack.config.ts

1
import * as path from "path";
2
import * as webpack from "webpack";
3
4
const DIST_DIR = path.resolve(__dirname, "dist");
5
const SRC_DIR = path.resolve(__dirname, "src");
6
7
const config: webpack.Configuration = {
8
module: {
9
rules: [
10
{
11
test: /\.tsx?$/,
12
use: [
13
{
14
loader: "ts-loader",
15
options: {
16
allowTsInNodeModules: true,
17
},
18
},
19
],
20
},
21
],
22
},
23
resolve: {
24
extensions: [".js", ".json", ".jsx", ".ts", ".tsx"],
25
},
26
devServer: {
27
contentBase: [DIST_DIR, SRC_DIR],
28
https: true,
29
port: 9090,
30
},
31
};
32
33
export default config;
Copied!
Last modified 5d ago