Quickstart
Goal: get one working component from your terminal into your site’s Drupal Canvas component library.
What you’ll have when you’re done
Section titled “What you’ll have when you’re done”- A local component project scaffolded from the Nebula template: Canvas Workbench (a Storybook-style local preview on a Vite dev server that auto-configures the JSON:API client), the Canvas CLI, an ESLint config that validates components against Canvas requirements, and AI-agent rules in
.agents/skills/. - One minimal component, validated and built locally.
- That component pushed to your site and visible in Canvas.
Prerequisites
Section titled “Prerequisites”- A Source CMS site you can administer: the
Editbutton (top right on any page of the site) opens the Canvas visual editor. (No site? A self-serve Cloud Platform free trial gets you building on Acquia the same day, though its Drupal 11 application is the Cloud Platform path, not a Source CMS site; for one of those, request a Source demo.) - Node.js 22 (the scaffold pins it in
.nvmrc; the dev server requires 20.19+ or 22.12+). npm ships with Node. - git
-
Scaffold the project from the Nebula template
Section titled “Scaffold the project from the Nebula template”Terminal window npx @drupal-canvas/create@latest my-components \--template acquia-nebula \--agents none--agents noneskips generating extra per-agent rule files (for Cursor, Copilot, and similar); the template’s own.agents/skills/rules are included regardless, and you can generate agent-specific files later if you want them.The tool fetches the template, installs dependencies with npm, and initializes a git repository:
┌ Drupal Canvas Create│◇ Fetched template│● No additional agents selected. Skipping compatibility setup.│◇ Installed dependencies with npm│◇ Initialized Git repository on main with initial commit│◇ Get started ────────────────────────╮│ ││ Created project in ./my-components ││ ││ Next steps: ││ cd my-components ││ npm run dev ││ │├──────────────────────────────────────╯│└ Canvas project created successfully.The scaffolded tree (trimmed):
my-components/├── .agents/skills/ # AI rules: canvas-*, nebula-*├── .claude/skills/ # symlinked for Claude├── .env.example # Canvas CLI credentials├── .github/workflows/ # lint-pr.yml, tests.yml├── .nvmrc # 22├── AGENTS.md├── canvas.config.json # component/page dirs, CSS, sync├── eslint.config.js # @drupal-canvas/eslint-config├── examples/│ ├── components/ # 25 examples to copy from│ ├── pages/homepage.json│ └── regions/ # header.json, footer.json├── package.json # "dev": "canvas-workbench"└── src/├── global.css # Tailwind CSS entry point└── layout.jsx -
Start the local preview
Section titled “Start the local preview”Terminal window cd my-componentsnpm run dev> [email protected] dev> canvas-workbenchVITE v7.3.2 ready in 336 ms➜ Local: http://localhost:5173/Open
http://localhost:5173/. Canvas Workbench scanssrc/components/for components and renders live previews. It’s empty right now; your first component appears here in a moment. (Theexamples/components/directory is reference material to copy from, not part of the scanned library.) Leave it running. -
Scaffold one minimal component
Section titled “Scaffold one minimal component”In a second terminal, from the project root:
Terminal window npx canvas scaffold --name my-hero┌ Drupal Canvas scaffold│◇ Created component││ Created: my-hero│ Directory: src/components/my-hero│ Component metadata: src/components/my-hero/component.yml│ Source file: src/components/my-hero/index.jsx│ CSS file: src/components/my-hero/index.css│└ Scaffold completedindex.jsxis the component itself: ordinary React, a default export, props with default values, and acontentslot the editor fills with other components.src/components/my-hero/index.jsx /*** MyHero example component*/const MyHero = ({greeting = 'Hello world!',ctaDisplayText = 'Click me!',ctaLink = 'https://example.com',content,}) => {return (<div className="my-hero-component"><h2 className="greeting">{greeting}</h2><div className="content">{content}</div><button type="button" className="cta"><a href={ctaLink}>{ctaDisplayText}</a></button></div>);};export default MyHero;Open
src/components/my-hero/component.ymland change the second line frommachineName: hello-worldtomachineName: my-hero. The scaffold writes a placeholder machine name, and it must match the directory name. -
Validate the component
Section titled “Validate the component”Terminal window npx canvas validate┌ Drupal Canvas validate│◇ Validated components││ Valid: my-hero│└ Validation completedThe new component also appears in Workbench, rendered with the example values from
component.yml. -
Create Canvas CLI credentials on your site
Section titled “Create Canvas CLI credentials on your site”In your site’s admin UI, go to
API > API clientsand selectAdd API clientto create an API client. Enter a name, select theClient Credentialscheckbox, addcanvas:asset_libraryandcanvas:js_componentas scopes, and set aUser(not the anonymous user) to serve as the author of the client’s actions. Save to generate the credentials. (Same flow as the auth quickstart, with Canvas scopes.)Copy
.env.exampleto.envand fill in the values. The Canvas CLI reads these exact variable names, a separate OAuth client from theDRUPAL_*credentials the Content API uses, since it pushes components rather than reading content:.env CANVAS_SITE_URL=https://your-site.example.comCANVAS_CLIENT_ID=your-client-idCANVAS_CLIENT_SECRET=your-client-secret -
Push the component to your site
Section titled “Push the component to your site”Terminal window npx canvas pushThe CLI plans the change (
Components: 1 create) and asksPush these changes to https://your-site.example.com?; confirm withYes. It then buildsmy-herotogether with the global Tailwind CSS assets and uploads everything:◇ Pushed components││ Created: my-hero│◇ Prepared assets│◇ Pushed assets││ Global CSS (Tailwind CSS build)│└ ✓ Push completed -
Enable the component
Section titled “Enable the component”canvas scaffoldwrotestatus: falseintocomponent.yml, and the flag survived the push:my-herois on the site but disabled, and a disabled component cannot be placed on a page. Changestatus: falsetostatus: trueinsrc/components/my-hero/component.yml, then push again and confirm:Terminal window npx canvas push -
Confirm the component is in the site’s component library
Section titled “Confirm the component is in the site’s component library”Open any page of your site (the homepage works), click
Edit(top right) to open Canvas, then click theCode (</>)icon in the left toolbar.My Herois listed among the code-based components; select it to open it in the code editor with itsPreviewandComponent datapanels. It is now available to drag onto pages from the component library.
What just happened
Section titled “What just happened”The Nebula template gave you a complete authoring environment. Canvas Workbench previews components locally on a Vite dev server that auto-configures the JSON:API client for requests against your Drupal site (mirroring the in-browser code editor). ESLint enforces the rules a component must satisfy to run in Canvas, and .agents/skills/ carries rules that teach AI coding agents the same conventions. canvas push compiled your React source, then uploaded the component (scope canvas:js_component) and the built CSS assets (scope canvas:asset_library) to your site, where it joined the component library alongside components created in the browser editor. (To remove a pushed component later, delete its directory from src/components/ and run npx canvas push again: the plan lists it as a delete.)
Your site’s library was empty, which is the one case where a push cannot take anything away. Against a site that already has components and pages, push reconciles the two sides and can plan deletes: read push to a live site before the next one.
This page was verified against @drupal-canvas/create 1.5.0 and @drupal-canvas/cli 0.20.1. The current template ships Canvas Workbench (its Storybook-style successor) and agent skills in .agents/skills/; older descriptions of the Nebula scaffold naming Storybook and a ruler AI-rules folder are out of date.
When something goes wrong
Section titled “When something goes wrong”You are using Node.js 18.20.4. Vite requires Node.js version 20.19+ or 22.12+. Please upgrade your Node.js version.
npm run dev was started with an unsupported Node version. The project pins Node 22 in .nvmrc; run nvm use (or install Node 22) and start again.
Authentication Error: Client authentication failed (from canvas push)
the client ID or secret in .env is wrong, or the API client isn’t set up for this flow. Re-copy both values into .env from the client at API > API clients, and confirm the client has the Client Credentials checkbox selected with both canvas:asset_library and canvas:js_component scopes. If it still fails after that, the secret was likely lost or regenerated: create a fresh client as in step 5 and put its new ID and secret in .env.
API endpoint not found: /oauth/token with “Canvas module is not enabled”
the CLI reached a server, but not a Canvas-enabled site. Check CANVAS_SITE_URL points at your Source CMS site (no trailing slash, no path); the usual cause is a URL for some other server entirely.
Line 2, Column 14: Directory name "my-hero" does not match machineName "hello-world". (drupal-canvas/component-dir-name)
(from canvas validate): the scaffold’s placeholder machine name was left in place. Edit component.yml so machineName matches the component’s directory name.
✗ Push incomplete
(from canvas push): the site already had components or pages that aren’t in your project’s src/components/. canvas push syncs the site to match your project exactly, including deleting anything it doesn’t find locally; if one of those items is placed on a page or referenced by other configuration, Drupal refuses to delete it and the push fails. The push is not rolled back: whatever the run already applied stays applied, so read its Created: / Updated: / Deleted: lines to see what landed. To avoid the conflict, pull the site’s existing components into your project before pushing (push to a live site).
canvas push prompts Enter the site URL instead of pushing
the .env file is missing or not in the project root. Copy .env.example to .env in the directory you run the command from.
Next steps
Section titled “Next steps”- Push to a live site: what the next push does to a site that already has pages on it, and how to get back if one goes wrong.
- Component development: props, slots, Tailwind styling, composition patterns, and AI-assisted authoring.
- Render CMS content with content templates: make components display entity fields instead of typed-in values.
- Version pages and global regions: pull the site’s structure into this same project.
- Canvas CLI & schema reference: every command and the full component schema.
Was this page helpful?
What went wrong?
Still stuck? Contact Acquia Support (opens in a new tab)