What Drupal single-directory components do for component theming
Drupal single-directory components let a team keep one card, banner or teaser as a single unit of template, CSS, JavaScript and metadata, then reuse it wherever the markup appears. A team needs them when the same markup has been copied into several Twig templates, Layout Builder configurations or block templates, and each copy drifts a little further from the others. Each component lives in its own directory, declares the data it accepts as props and the regions it accepts as slots, and is rendered by name instead of being pasted. The result is one place to fix a bug, change a class or update a field label, with every consumer picking up the change on the next build. This page covers the design decisions, the setup sequence, the operating model and the limits of the approach. The Drupal Canvas and Drupal CMS facts supplied for this topic matter only where a team plans to let content editors place components visually or adopt a distribution, so they are treated as conditional rather than as the starting point.
Before you adopt the pattern, decide which markup actually qualifies as a component. Good candidates are elements with a stable data contract, such as a card that always receives a title, an image and a link. Poor candidates are one-off page layouts that change with every campaign, because each change then becomes a component version change across consumers. Confirm with your Drupal version and the component module you plan to use how the feature is provided and which release line it belongs to, since this page does not state version numbers for the feature. Also confirm how your current theme is built. A team that has subthemed a core theme will need to decide whether components replace, wrap or sit beside existing Twig templates, and that decision sets how much markup has to move.
How a single-directory component is structured and why props and slots matter
A single-directory component keeps everything for one piece of UI in one folder: a Twig template, an optional stylesheet and script, and a component definition file ending in .component.yml. The definition file is where the component's contract lives. It declares each prop with a type, an optional default and any allowed values, and it declares named slots. Keep logic that belongs in the schema out of the template, so a reviewer can open the folder and see what the component accepts without reading every line of markup. Before you write any component, confirm which Drupal core version provides the component system in your environment and whether a contributed module must be installed alongside it, because file conventions and schema behavior depend on that version.
Props carry data such as a heading, a link URL or a variant name. Typed props make those values predictable for editors: a boolean stays a boolean, an enumerated value only offers the options you listed, and a required value that is missing fails validation instead of publishing an empty block. Slots carry markup. A card component can accept an image, a paragraph of formatted text or a nested list in a slot without the component needing to know what that markup contains. Use slots for structure and props for data. Before release, render the component with minimum, typical and maximum prop values and with an empty slot, then compare the output against the design.
The same component can be placed in more than one way. A site builder can add it in Layout Builder or Drupal Canvas and fill its props through a generated form, while a developer can render it from another Twig template with an include or embed call and pass values from template variables. Both paths should read the same definition file, so the form an editor sees and the arguments a developer passes stay aligned. Decide early which props are editor-facing and which exist only for code, because every editor-facing prop becomes a field someone must understand and maintain. Test each placement path on a staging copy of the site, since the editing experience and rendering behavior can differ between Layout Builder and Canvas.
Which component pattern fits each template need
This table compares four ways to build a Single-Directory Component, from static markup to components that load their own JavaScript. Read each row as a decision: when the pattern fits, what it adds to build and test effort, and what tends to break when Drupal core or the theme changes.
| Pattern | When it fits | Build and testing effort | What breaks on upgrade |
|---|---|---|---|
| Static markup only | Fixed page furniture such as a site footer, a logo block or a divider that never changes per use | Lowest. One Twig template and a stylesheet to review in the browser | Little beyond Twig changes in core markup or theme hooks the template extends |
| Typed props only | Cards, teasers and buttons that show the same structure with different text, links or image fields | Moderate. Each prop needs a documented type and default, and every Twig call site must be checked | Renamed or removed props when the component schema changes, and call sites that pass stale values |
| Props with slots | Containers such as a panel or accordion that wrap editor-built or Layout Builder content of unknown shape | Higher. Each slot needs a defined position, fallback markup and tests with varied nested content | Slot names and wrapper markup that templates and Layout Builder output depend on, so renames ripple into saved layouts |
| Component with its own JavaScript | Interactive widgets such as tabs, carousels or filters that must work wherever the component is placed | Highest. JavaScript needs attachment behavior, library dependencies and browser tests in addition to Twig tests | Behaviors that stop firing after core JavaScript or jQuery changes, and library load order conflicts with other modules |
| Page-level Twig template | Full page layouts that combine several components and are not reused in other contexts | Low to moderate. Fewer variants, but any change touches every page using the template | Changes to base templates in core or the parent theme that the page template overrides |
Reusing one component across Layout Builder, Canvas and Twig
Layout Builder lets editors place blocks into sections of a single node or a template layout. When a Single-Directory Component is exposed as a block or as a field formatter, editors choose the component and fill in the props the developer has exposed, usually through a form generated from the component's metadata. The developer keeps the component's template, CSS and JavaScript in one directory and decides which props are editable and which are fixed. The rendered markup, class names and asset loading stay the same as on any other consumer, so a card looks identical whether it sits in a landing page section or in a Twig template.
- Editor controls: the values of exposed props such as heading, link and image, plus the section and block order.
- Developer maintains: the prop schema, default values, validation rules and the block plugin or field wiring that exposes the component.
- Renders identically: the template structure, class names, slot markup and the component's CSS and JavaScript.
- Watch for: editors overriding layout inside each section, which can drift from the design system over time.
- Watch for: Layout Builder overrides that are stored per node, so a component change may not reach pages already built.
Confirm with your team which props the editors can change and whether existing Layout Builder overrides need a migration when the component changes.
Drupal Canvas, previously Experience Builder, is a visual page builder in which editors assemble pages from components and set their props through a canvas interface. The Drupal CMS distribution is one context where Canvas is used, and it includes recipes and site templates for marketing and content teams. Canvas availability depends on the Drupal version and the Canvas release you run, so confirm that Canvas supports your Drupal core version and the component format you intend to use before you start the build.
- Editor controls: component selection, placement and the props the component exposes in the Canvas interface.
- Developer maintains: the component definition, prop types, slot names, example values and the compatibility of those definitions across Canvas releases.
- Renders identically: the component template and asset output, since Canvas renders the same component files used elsewhere.
- Watch for: prop types that Canvas cannot represent, which force a change in the component's interface.
- Watch for: Canvas changes between releases, which may require updates to component definitions.
Confirm Canvas availability and the supported component definition format with your Drupal version before you commit to Canvas-driven page building.
In a Twig-driven site, developers place the component directly in a template, a view display or a preprocess result, and pass the props as an array. Editors do not choose the component. They only fill in the content fields that feed those props, so the component's behavior is controlled by code. This is the most predictable consumer for layout decisions, and it is the consumer where the component's props and slots are most visible in the code review.
- Editor controls: the content entered in node fields that the template maps into props.
- Developer maintains: the include or embed calls, the prop arrays passed from preprocess functions, and slot content.
- Renders identically: the same template, CSS and JavaScript files used by the other two consumers.
- Watch for: prop names that differ between templates, which create silent failures when a prop is missing.
- Watch for: JavaScript attached to the component being loaded once per page, which matters when the component appears several times.
Ask your team to add a test or a review step that checks every Twig call site against the component's prop schema before each release.
Setting up a component library in a Drupal theme
This sequence takes a team from a confirmed Drupal core and PHP baseline to a first Single-Directory Component that renders in Layout Builder and Twig, with a check at each step so the library can be extended without rework.
Confirm core version and PHP baseline
The lead developer records the Drupal core version the site runs and the PHP version on every environment, including local and CI. Drupal 11 requires PHP 8.3 or later, and Drupal 10 requires PHP 8.1 or later, so a site on Drupal 10 with an older PHP runtime needs that gap closed before any theme work starts. Drupal 10 reaches end of life on December 9, 2026, which makes the target version a decision to record now. Done when the version matrix is written down and
php -vanddrush statusreturn matching values on each environment.Choose the theme and generator approach
Decide whether the component library lives in a new custom theme generated with the core Starterkit generator or in an existing theme that already has component support. Confirm with the team which theme engine and component module versions the project will pin, and record why an existing core-based subtheme is or is not acceptable for upgrade cost. Done when the theme name, its location in the codebase and the pinned dependency versions are committed in a decision note that the team has reviewed.
Create the first component with props and a slot
Create a single directory for one simple component, such as a card or notice, containing its Twig template, a component definition file that declares props with types and defaults, and a named slot for body content. Keep the first component small so that the prop contract is easy to review. Done when the component renders with a sample props file in the component's preview and the slot content appears in the expected location in the markup.
Add CSS and JavaScript with library dependencies
Place the component's stylesheet and script inside the same directory and declare them as libraries that the component depends on, so Drupal loads them only when the component is used. Include any design tokens from the theme rather than hard-coding values. Done when the page source shows the component's CSS and JavaScript attached once, and removing the component from a test page removes those assets from the request.
Wire the component into Layout Builder and Twig
Expose the component to Layout Builder so editors can place it, and call it from a Twig template with the same props so developers can reuse it in theme output. Test both paths with the same content so the markup matches. Confirm which Drupal capabilities the site uses before relying on a given integration path, and note that Drupal Canvas is a separate visual page builder that the team should evaluate on its own terms. Done when the component appears in Layout Builder with editable props and the Twig call produces identical markup for the same input.
Verify rendering and caching
Clear caches, then load the page as anonymous and authenticated users and compare output. Check that cache tags change when the underlying content changes, that the component updates after an edit, and that the page does not serve stale props after a configuration change. Run the same checks on a production-like build with the reverse proxy or CDN in place. Done when an edit to the source content appears on the page after the expected cache invalidation and no stale output remains.
Verify accessibility and record the library contract
Run an automated accessibility scan on the rendered component and check keyboard focus, heading order and text alternatives for any image slots, since automated scans miss some issues and a manual pass is still required. Record the prop names, types, slot names and required libraries in the component's documentation so future contributors and upgrades have a reference. Done when the scan reports no new issues against the baseline, the manual checklist is signed off and the contract document is committed alongside the component.
Security and operational risks in component markup and JavaScript
Components move markup, styles and scripts into shared libraries that many templates reuse, so one weak prop, slot or script can reach every page that renders it. The cards below cover the checks a team should make in its component code, its dependencies and its release process before components go live.
Escape editor-supplied prop values
Editors often enter values that become component props, such as link text, captions or badge labels. In Twig, auto-escaping protects these values by default, but any filter that marks a value as safe or any raw print of a prop bypasses that protection. Review every component template for the |raw filter and for props rendered inside attributes or script blocks, where escaping rules differ. Add a test that submits script markup through each text prop and confirms it renders as plain text.
Limit unsafe HTML passed through slots
Slots accept markup from the calling template, and that markup may come from a rich text field or a Layout Builder block. A slot that is printed without sanitization can carry script or event-handler attributes into the page. Decide which slots accept trusted markup from developers only and which accept editor content that must pass through the text format filters. Document the allowed content for each slot in the component definition so that theme developers do not pass unfiltered render arrays into a slot meant for plain text.
Scope JavaScript and the content security policy
Components that bring their own JavaScript add scripts to pages that may not need them, and each script must be allowed by the site's content security policy. Inline scripts and inline event handlers usually require policy exceptions or nonces, which weakens the policy for the whole site. Load component scripts as external files, attach them only when the component renders, and check each one against the policy headers set at the web server or CDN. Confirm the header configuration with your security team before adding any new script source.
Track dependencies and patches for contributed components
Some component libraries or helper packages come from Composer or npm, and some Drupal modules provide the component API itself. Pin versions in the lock files, record which packages each component depends on, and review changelogs before updating. Where a team carries a local patch to a contributed module, document the patch, the issue it addresses and the removal condition, so that the patch does not block the next upgrade or hide a security fix.
Follow the Drupal Security Team advisory process
The Drupal Security Team resolves reported vulnerabilities in core and contributed projects and publishes advisories with CVE identifiers and risk scores. Security releases are issued on a fixed Wednesday window, so the team needs a standing process to read advisories for every core and contributed project a component relies on, decide whether the fix applies, and deploy it within an agreed period. Assign an owner for this watch and record which advisories were reviewed and when.
Invalidate caches when component output changes
Rendered component output is cached by Drupal's render cache, page cache and any reverse proxy or CDN in front of the site. When a template, stylesheet or script changes, the cached markup can keep serving the old version, or a component cached per user or per node can show stale content after an update. Set cache tags and contexts on components that depend on entities, run cache rebuilds as part of every deployment, and purge proxy and CDN caches for the asset paths that changed. Include a check in the release checklist that confirms the new output appears on a representative set of pages.
Questions buyers ask about component theming effort and ownership
What drives the effort of building a Drupal component library?
The main driver is how many distinct components the site needs and how much each one varies across pages, content types and breakpoints. Effort rises with the number of props and slots each component exposes, the number of states it handles (empty, loading, error, translated), and how many places it must render: Layout Builder, Canvas and plain Twig templates. Existing markup and CSS that was written without component boundaries also adds time, because each block has to be untangled before it can be packaged. Ask any partner to list the components by name, with their props, slots and placements, before agreeing a scope.
Can our in-house team maintain a Single-Directory Components library after launch?
Yes, if the team has someone comfortable with Twig, CSS and JavaScript, and if the library has clear ownership, a review process and tests for each component. The recurring work is small in shape but constant: fixing component regressions after Drupal core or contributed module updates, adding variants for new content, and keeping documentation current. Teams that skip the documentation and test coverage usually find that only the original author can change a component safely. Agree who owns each component, how changes are reviewed, and what happens when a designer asks for a new variant.
How long does a first set of components take to build?
No duration can be given without knowing the scope, so the answer depends on the factors above: component count, existing design assets, how many display contexts each component must support, and how quickly decisions come from design and content owners. A fixed-scope agreement should state the components in scope, the environments they must work in and the acceptance checks, which is what turns an open-ended estimate into a timeline. Ask for the phases and the dependencies between them in writing.
What does the client need to provide before component work starts?
Most of the input comes from the client. Typical items include design tokens (colors, spacing, typography and breakpoints) in a format the team can read, existing templates and CSS that will be replaced or reused, a list of content types and the fields each one carries, and access to a development environment with Drupal administrator rights to enable and configure the modules involved. Sign-off from one named design owner and one content owner also saves time, because unresolved variant questions stall the build. Confirm each item and its owner in the scope document.
How do components affect a later Drupal 11 to Drupal 12 upgrade?
Components reduce upgrade risk when they depend on Drupal's documented extension points and avoid overriding core theme markup directly. They add risk when they rely on deprecated APIs, on subthemes of core themes, or on module behavior that changed between releases. Drupal 12 is scheduled for the week of December 7, 2026, and it requires PHP 8.5 or later, so the component code, the libraries it loads and the theme's build tooling all need checking against those requirements. Use the Upgrade Status module and automated tests against the components before the upgrade begins, and confirm the compatibility of any third-party component library your team adopts.
What should a fixed-scope quote for component theming list?
A useful quote lists the components in scope by name, the props and slots each one exposes, the display contexts that must render them (Layout Builder, Canvas, Twig templates), the Drupal and PHP versions targeted, and the testing approach, including visual or browser checks where agreed. It should also state the client inputs and their owners, the handover materials such as documentation and a component inventory, the acceptance criteria, and what is excluded, such as new design work or content migration. Separate one-time build work from any recurring support or upgrade work, so the ongoing cost is visible before the project starts.
Does DeployOpen maintain the component library for us after handover?
DeployOpen offers ongoing support, upgrades and continuous testing as retainers, separate from the fixed-scope build. A retainer can cover updates to components when Drupal core or contributed modules change, and it can cover testing before each Drupal release. Ask for the support scope in writing, including response expectations and which components and environments it covers, because these are not included in the build agreement by default.
Limits to confirm in your environment before committing
Single-Directory Components sit on top of Drupal core, Canvas, Layout Builder and contributed modules, and each of those moves on its own release cycle. The cards below list the points we would confirm on your own Drupal version and test site before you commit a component library to a project.
Component system and module status
Confirm which Drupal version you run, whether the Single-Directory Components capability is in core or provided by a contributed module, and what stability status that code carries for your version. Read the project page and issue queue on Drupal.org, then check the release notes against your planned Drupal upgrade path. Record the exact version numbers you tested so the claim is tied to a build, not a memory of a demo.
Canvas and Layout Builder coverage
Ask whether the components you build can be placed in Drupal Canvas and in Layout Builder, and what props, slots and field mappings each editor path accepts. The two paths may expose different controls, so test editing a real page with your content types in each. Note any component that works only in Twig templates, because editors will expect it everywhere they place content.
JavaScript framework and build tooling
Confirm which JavaScript framework, bundler and CSS build step your team uses, and whether the component system supports them without custom glue. Check how component assets are compiled, versioned and loaded, and whether your Node version and package manager match the project's requirements. Keep the build pipeline in your repository so a toolchain change does not force a rewrite of every component.
Contributed theme and module dependencies
List every contributed theme and module your site uses to render markup, forms or media, and check each one for compatibility with the component approach you pick. Shared base themes and preprocess hooks often assume the classic Twig layout, so a component may render differently under them. Confirm each dependency's supported Drupal versions on its Drupal.org project page before you lock the stack.
Browser and accessibility targets
Agree the browsers, devices and assistive technologies your components must support, and test the markup for keyboard use, focus order and landmark roles in each one. Components that bundle their own CSS and JavaScript can change focus styles or break tab order when placed in a new layout. Ask your accessibility lead to sign off on the target list before the component library grows.
How DeployOpen works on this
DeployOpen is a Chainscore Labs company that plans, builds, hardens and supports self-hosted open-source platforms inside your own infrastructure. We approach Single-Directory Components as a scoped piece of theme work: the component inventory, prop and slot contracts, and reuse rules are agreed in writing before the build starts.
How we scope
- We inventory existing Twig templates, CSS and JavaScript to decide which markup becomes a component and which stays in the theme.
- We agree the component list, prop and slot contracts, and the Layout Builder, Canvas and Twig usage in a written, fixed-scope document.
- Scope names the Drupal version, the theme approach and the reviewers who will sign off each component before work starts.
How we build
- We build components as Single-Directory Components with colocated templates, styles and scripts, following the Drupal theme structure your team will maintain.
- We write automated tests and static checks for component output and review each change against Drupal coding standards before merge.
- We document each component's props, slots and dependencies so developers can reuse them without reading the full theme source.
How we hand over
- Handover includes the component library, a usage guide per component, the build and test commands, and the repository in your own source control.
- We walk your developers through adding a new component, changing a prop contract and deprecating an old one safely.
- Deliverables are listed in the written scope, so you can confirm what you receive before the engagement closes.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers, covering Drupal core and contributed module updates that affect the theme.
- Support can cover checks against each Drupal release, so component changes are tested before a major version upgrade is scheduled.
- Ask us to confirm response expectations, update cadence and included hours in the retainer terms before you sign.

