03 / How Lupus works
External components and Canvas
Canvas ExtJS registers external JavaScript components as a Canvas component source. It is not the renderer for all Drupal and is not required for a classic CE insight.
All walkthroughs ↗1. Define the catalogue
catalog.ts declares names, properties, examples and slots in an explicit TypeScript schema. The build publishes component-index.json. This POC does not automatically extract types or props from React functions.
2. Register in Drupal
Canvas ExtJS imports the index and creates extjs.* definitions. The editor knows which fields can change and where components can be nested. Breaking contract changes require coordination and migration.
3. Preview with React
A custom Custom Elements Preview Provider plugin loads the frontend React bundle and CSS inside Canvas previews. Hero, Section, Card and CTA share their implementation with the website. The React provider and its build are specific work for this Next.js copy.
4. Publish and render
Canvas stores the composition. CE API delivers it as elements, props and slots. In Next.js, CeRenderer finds NttHero or NttSection in its registry and renders children. A matching Twig template is not needed for each editorial component.
Real example from this installation
The real JSON shows NttSection and its items slot with three NttCard elements. Open Canvas and select the Hero to inspect properties, or consult the index of the seven registered components.
Inspect the contract received on the server
{
"title": "NTT DATA - Connected experiences",
"content_format": "json",
"content": {
"element": "canvas-page",
"slots": {
"components": [
{
"element": "NttHero",
"props": {
"eyebrow": "NTT DATA / Digital experience lab",
"title": "One experience. Every possibility.",
"description": "Drupal keeps content and rules. Custom Elements defines delivery. Next.js presents a connected experience, with Canvas and beyond Canvas.",
"href": "/demo/insights",
"linkText": "Explore the experience"
}
},
{
"element": "NttSection",
"props": {
"eyebrow": "01 / One frontend, different sources",
"title": "Drupal decides. Next.js presents.",
"description": "Visual composition, structured pages, Views and modules with blocks: discover what each layer does and verify it through real examples."
},
"slots": {
"items": [
{
"element": "NttCard",
"props": {
"eyebrow": "Canvas / Visual composition",
"title": "External components, editorial composition.",
"description": "React heroes, sections and cards registered with Canvas ExtJS. A property and slot contract connects the frontend catalogue to the editor.",
"href": "/demo/guide/components",
"linkText": "Explore Canvas and ExtJS"
}
},
{
"element": "NttCard",
"props": {
"eyebrow": "Drupal / Structured content",
"title": "One entity. Different ways to present it.",
"description": "Drupal configures fields and formatters in CE displays. The same entity is delivered as a full page and teaser, without a Canvas template.",
"href": "/demo/digital-experience",
"linkText": "Discover the case"
}
},
{
"element": "NttCard",
"props": {
"eyebrow": "Views / Dynamic selection",
"title": "The query stays in Drupal.",
"description": "A real View selects seven published insights and returns results and a pager. Next.js presents three items per page.",
"href": "/demo/insights",
"linkText": "Explore the insights"
}
}
]
}
},
{
"element": "NttCta",
"props": {
"title": "More Drupal. One frontend.",
"description": "Understand Lupus Decoupled, Custom Elements, CE API and ExtJS. Explore data delivery versus presentation delivery, with module and block examples.",
"href": "/demo/guide",
"linkText": "How Lupus works"
}
},
{
"element": "NttCta",
"props": {
"title": "One experience.",
"description": "Editorial freedom in Drupal.",
"href": "/demo/insights",
"linkText": "Explore"
}
}
]
}
},
"metatags": {
"meta": [],
"link": [
{
"rel": "alternate",
"hreflang": "en",
"href": "https://dev-headless-lupus.pantheonsite.io/en"
},
{
"rel": "alternate",
"hreflang": "es",
"href": "https://dev-headless-lupus.pantheonsite.io/"
}
]
},
"blocks": {
"sidebar_first": {
"canvas_lupus_theme_page_title": {
"element": "drupal-markup",
"slots": {
"default": "\n\n"
}
},
"canvas_lupus_theme_powered": {
"element": "drupal-markup",
"slots": {
"default": "<span>Powered by <a href=\"https://www.drupal.org\">Drupal</a></span>"
}
}
},
"header": {
"canvas_lupus_theme_site_branding": {
"element": "drupal-markup",
"slots": {
"default": "<img src=\"https://dev-headless-lupus.pantheonsite.io/themes/custom/canvas_lupus_theme/logo.svg\" alt=\"Home\" />\nCanvas Lupus POC"
}
}
},
"primary_menu": {
"canvas_lupus_theme_main_menu": {
"element": "drupal-markup",
"slots": {
"default": "\n <ul>\n <li>\n <a href=\"/en\" data-drupal-link-system-path=\"<front>\">Home</a>\n </li>\n </ul>\n \n\n"
}
}
},
"secondary_menu": {
"canvas_lupus_theme_account_menu": {
"element": "drupal-markup",
"slots": {
"default": "\n <ul>\n <li>\n <a href=\"/en/user/login\" data-drupal-link-system-path=\"user/login\">Log in</a>\n </li>\n </ul>\n \n\n"
}
}
},
"highlighted": {
"canvas_lupus_theme_messages": {
"element": "drupal-markup",
"slots": {
"default": "<div data-drupal-messages-fallback class=\"hidden\"></div>"
}
}
}
}
}