@canva/app-scripts
v1.1.1
Published
Build scripts and bundler configuration for Canva Apps
Maintainers
Readme
@canva/app-scripts
Build scripts for Canva Apps.
@canva/app-scripts is designed to be used through the Canva CLI: canva apps start and canva apps build are the recommended way to run and build your app. Prefer working with the package directly? Its own CLI commands and programmatic API are there for you too.
Installation
npm install -D @canva/app-scriptsHave a Canva App that isn't using @canva/app-scripts yet? You probably have a webpack.config.ts and a scripts/start/ runner at its root - see the migration guide to make the switch.
Configuration
Create an optional canva-app.config.ts file in your project root to override defaults (.js, .mjs, .cjs, .mts, and .cts are also supported; TypeScript is recommended):
Basic Configuration
import { defineConfig } from "@canva/app-scripts";
export default defineConfig({
entry: "./src/index.tsx",
});Bundler Selection
By default, @canva/app-scripts uses Rsbuild as the bundler. You can explicitly select a bundler:
import { defineConfig } from "@canva/app-scripts";
export default defineConfig({
bundler: "rsbuild", // Default, can be omitted
entry: "./src/index.tsx",
});Bundler support:
rsbuild- Default bundlerwebpack- Supported alternative
Customizing the Bundler
Use the config function to customize the underlying bundler configuration. The callback is typed per bundler, so bundler and config travel together, and the configuration types are sourced from the respective bundler's own package (@rsbuild/core, webpack).
Rsbuild (default)
import { defineConfig } from "@canva/app-scripts";
import type { RsbuildContext } from "@canva/app-scripts";
import type { RsbuildConfig } from "@rsbuild/core";
export default defineConfig({
config: (rsbuildConfig: RsbuildConfig, { mode }: RsbuildContext) => {
// Disable source maps for production builds
if (mode === "production") {
rsbuildConfig.output = { ...rsbuildConfig.output, sourceMap: false };
}
return rsbuildConfig;
},
});webpack
import { defineConfig } from "@canva/app-scripts";
import type { WebpackContext } from "@canva/app-scripts";
import type { Configuration } from "webpack";
import path from "path";
export default defineConfig({
bundler: "webpack",
config: (webpackConfig: Configuration, { mode }: WebpackContext) => {
// Add SVG loader - import SVGs as React components
webpackConfig.module?.rules?.push({
test: /\.svg$/,
issuer: /\.[jt]sx?$/,
use: [
{
loader: "@svgr/webpack",
options: {
svgoConfig: {
plugins: [{ name: "removeViewBox", active: false }],
},
},
},
],
});
// Add path aliases for cleaner imports
webpackConfig.resolve = {
...webpackConfig.resolve,
alias: {
...webpackConfig.resolve?.alias,
"@components": path.resolve(__dirname, "src/components"),
"@assets": path.resolve(__dirname, "src/assets"),
},
};
// Enhanced source maps for development
if (mode === "development") {
webpackConfig.devtool = "eval-source-map";
}
return webpackConfig;
},
});Note: Custom loaders like
@svgr/webpackmust be installed separately:npm install --save-dev @svgr/webpack
The config function receives:
- The base bundler configuration (
RsbuildConfigfrom@rsbuild/core, orConfigurationfromwebpack) context- Build context withmode("development"or"production")
Backend
If your app has a backend, set backend to run it alongside the dev server. It runs under nodemon with TypeScript support, and a backend crash tears the dev server down. Any backend config (even {}) enables it.
import { defineConfig } from "@canva/app-scripts";
export default defineConfig({
entry: "./src/index.tsx",
backend: true, // runs backend/server.ts
});Pass an object to override the defaults:
| Option | Description | Default |
| -------- | --------------------------------------------------------------------------- | ------------------- |
| entry | Backend entry, relative to the project root. | backend/server.ts |
| port | Port the backend listens on. Falls back to CANVA_BACKEND_PORT. | 3001 |
| host | Backend host the built app calls. Falls back to CANVA_BACKEND_HOST. | — |
| tunnel | Expose the backend through a public HTTPS tunnel. Overridden by --tunnel. | false |
export default defineConfig({
backend: { entry: "./api/main.ts", port: 3100 },
});Dev Server
Optional dev-server options. CLI flags and environment variables override these at run time.
| Option | Description | Default |
| ------- | ------------------------------------------------------------------- | ------- |
| port | Frontend dev-server port. Overridden by --override-frontend-port. | 8080 |
| https | Serve the frontend over HTTPS. Overridden by --use-https. | false |
export default defineConfig({
devServer: { port: 9000, https: true },
});Translations
Optional configuration for extract-translations.
| Option | Description | Default |
| ------------ | ------------------------------------------- | ------------------- |
| outputDir | Directory to write extracted messages into. | dist |
| outputFile | Output filename (no directory segments). | messages_en.json |
| pattern | Glob for source files to scan. | src/**/*.{ts,tsx} |
export default defineConfig({
extractTranslations: { outputDir: "i18n", pattern: "app/**/*.tsx" },
});Environment Variables
These environment variables are supported but optional. Most can also be configured in canva-app.config.ts (recommended); some remain env-only.
| Variable | Description | When needed |
| --------------------- | ------------------------------------------------------------- | ---------------------------------- |
| CANVA_FRONTEND_PORT | Dev server port (default 8080). Overrides devServer.port. | Optional — prefer devServer.port |
| CANVA_BACKEND_PORT | Backend server port (default 3001). Overrides backend.port. | Optional — prefer backend.port |
| CANVA_BACKEND_HOST | Backend URL the built app calls. Overrides backend.host. | Optional — prefer backend.host |
| CANVA_HMR_ENABLED | Enable Hot Module Replacement (true/false). | Optional |
| CANVA_APP_ORIGIN | App origin URL. | Required for HMR |
| CANVA_APP_ID | Canva app ID. | Required when running a backend |
| NGROK_AUTHTOKEN | ngrok authtoken. | Required when using --tunnel |
Configuration precedence
When an option can be set in more than one place, highest priority wins: CLI flag > environment variable > canva-app.config.ts > package default. Not every option exists in every channel; the CLI prints a Warn: when an env var shadows a config value.
Design Philosophy
The public defineConfig() API is intentionally minimal:
entry,rootDir, andoutputDir- Essential app configurationbundler- Select the build tool (Rsbuild by default, webpack also supported)devServer- Dev-server options (port, HTTPS)backend- Run a backend alongside the dev serverextractTranslations- i18n extraction optionsconfig- Escape hatch for full bundler customization
Every option is overridable at run time via CLI flags and environment variables, so the config file holds what's stable to the project while the invocation controls the rest.
CLI Commands
The package provides a canva-app-scripts CLI with the following commands. The Canva CLI's canva apps start and canva apps build are the recommended way to run these; use canva-app-scripts directly if you'd rather not go through the Canva CLI.
dev
Start the development server. Serves the app from src/index.tsx.
npx canva-app-scripts devIf a backend is configured (or --backend-entry is passed), the backend entry is run alongside the dev server under nodemon, with SSL certificate paths passed through to its environment. A backend crash tears the whole dev server down. See Backend for the config options.
npx canva-app-scripts dev --entry ./src/app.tsx
npx canva-app-scripts dev --backend-entry ./backend/server.tsbuild
Build the app for production. Outputs to dist/ by default.
npx canva-app-scripts build
npx canva-app-scripts build --entry ./src/app.tsx --output-dir ./buildextract-translations
Extract i18n messages from your source files into dist/messages_en.json.
npx canva-app-scripts extract-translations
npx canva-app-scripts extract-translations --output-dir ./i18n --pattern "src/**/*.{ts,tsx}"
npx canva-app-scripts extract-translations --output-file messages.jsonGlobal CLI options
--entry- Entry file for the app (overridesentryfrom the config; ondevandbuild)--tunnel- Expose the backend through a public HTTPS tunnel (alias:--ngrok)--use-https- Start local development server on HTTPS--override-frontend-port- Override the frontend port--backend-entry- Path to the backend entry script (overridesbackend.entryfrom the config)
Changelog
See the CHANGELOG.md file.
Contributing
We're actively developing this package but are not currently accepting third-party contributions. If you'd like to request any changes or additions to the package, submit a feature request via the Canva Developers Community.
License
See the LICENSE.md file.
