Comparison and Decision Guide
|
This content was generated with the assistance of AI and should be verified against the official Drupal documentation before being relied on in production. This sub-section’s Drupal bibliography lists the reference material consulted while preparing these pages. |
Drupal offers several ways to do almost everything, and most of the early decisions of a project are a choice between two or three of them. This page gives a table and a rule of thumb for each such choice, then says when Drupal is the wrong tool altogether. It was checked on 2026-10-30 against Drupal 11.4.x, Drush 13 and 14, and PHP 8.3 or later. Drupal 10 reaches end of life on 2026-12-09 and Drupal 12.0.0 is planned for the week of 2026-12-07, so both are future dates from the point of view of this check; a new site starts on Drupal 11, and this page does not recommend Drupal 10.
The running example is the Meridian Museum, a fictitious regional science museum with exhibitions, events, news and a collection of objects, an English and Spanish site, and a gallery kiosk. Each section ends with the choice the museum makes. For cross-CMS metrics (market share, security record, talent pool, cost) see the CMS Comparison, which is not repeated here. The decisions below are about choices inside Drupal.
Drupal core versus Drupal CMS
Drupal core is the framework and a minimal installation profile. Drupal CMS is a curated distribution of Drupal 11
that is assembled from recipes (a recipe is a package of configuration and dependencies applied once; see
Recipes). It is not a fork: it is core plus contributed projects plus a pre-configured
experience. Version 2.x makes Canvas the default editor and offers the site templates Byte, Starter (with the Mercury
theme) and Blank, with an optional AI Assistant. Version 1.x is unsupported, and the documentation lives at
project.pages.drupalcode.org/drupal_cms. Recipes have no update path: once applied, the site is an ordinary Drupal
site, and you maintain it with Composer and Drush like any other. See
Drupal CMS and Site Templates.
| Criterion | Drupal core | Drupal CMS |
|---|---|---|
Starting point |
A minimal site (the Standard profile no longer creates the Article and Page content types in 11.4) |
A working site from a site template, with editor, SEO, forms and accessibility defaults chosen for you |
Editor experience |
Whatever you build: content forms, Layout Builder, Paragraphs, Canvas |
Canvas by default in 2.x |
Control and size |
You add only what you need; a smaller attack surface and a longer first build |
More modules and recipes on day one that you may remove or keep |
Learning value |
You see every decision |
Faster result; you still need core knowledge to change things |
Upgrades |
Composer, |
The same, because the result is ordinary Drupal |
Rule of thumb. Choose Drupal CMS when a small team wants a good-looking, editor-friendly site quickly and has no strong opinion about the architecture. Choose core when the data model is the product, as with the museum’s collection, or when the project must pass a review of every installed module. The museum starts from core because it has a custom content entity and a curated roles model; a smaller museum partner site could start from a Drupal CMS template.
Layout Builder versus Canvas versus Paragraphs
These three address the same editorial wish, a page that is more than one text field, in different ways.
-
Layout Builder is a core module. It places blocks and fields into sections, per content type or per node, and stores the layout in configuration (per content type) or in a field (per node). See Layout Builder.
-
Canvas (formerly Experience Builder) is a contributed module, at 1.0 since December 2025 and at 1.12 on the check date, and it requires Drupal 11.3 or later. It composes pages from Single Directory Components and from code components written in JavaScript and CSS and rendered with Preact through its React compatibility layer. See Canvas.
-
Paragraphs is a contributed module that adds reorderable, typed content chunks to an entity reference revisions field, so editors fill structured parts instead of laying out a page. Its current release works with Drupal 10.3 and Drupal 11.
| Criterion | Layout Builder | Canvas | Paragraphs |
|---|---|---|---|
Where it lives |
Core |
Contrib; Drupal CMS 2.x default |
Contrib |
Editing model |
Place blocks into regions of a section, per type or per node |
Drag components, edit props and slots in a live page |
Add, reorder and fill typed chunks in the node form |
Best for |
Landing pages, per-node layouts for a few content types |
Marketing and visitor-facing pages assembled by non-developers |
Long, structured, repeatable content (a gallery of objects, an accordion) |
Developer effort |
Block plugins, templates, access and cache care |
SDC components and code components |
Paragraph types, field formatters and templates |
Risk |
Per-node overrides are hard to migrate and hard to keep consistent |
Younger module; check release notes before relying on a feature |
Content and layout are tied to field structure and revisions |
Drupal CMS 2.0 had no automatic migration from Layout Builder or Paragraphs content to Canvas, so choose with the next three years in mind and re-check the Canvas release notes before planning a migration. Layout Builder stays in core. The older books Drupal 10 Masterclass (2023) and Drupal 10 Development Cookbook (2023) present Layout Builder as the only visual builder; since Canvas, it is one of three options, and the right default depends on who edits the page.
Rule of thumb. Use Paragraphs when the content is structured and repeated, Layout Builder when a few page types need
bespoke layout and you want to stay on core, and Canvas when non-developers assemble varied pages from a component
library. The museum uses structured content types and Views for exhibitions and objects, and Canvas for its home and
campaign pages, with the ticket-banner code component.
Custom module versus contrib
A contributed module is maintained by the community and reviewed by the Security Team when it has a stable release that opts in to security coverage. A custom module is code your team owns forever.
| Question | Prefer contrib when | Prefer custom code when |
|---|---|---|
Does it already exist? |
A maintained project with a stable release and Drupal 11 support does the job |
No project fits, or the fit needs patches you cannot upstream |
Who maintains it? |
Many sites depend on it, with active releases |
It encodes your own domain, such as |
Security |
A stable release covered by Drupal’s security advisories policy |
You accept the review, testing and update burden |
Cost |
Install, configure, track its updates |
Design, write, test (see Automated Testing) and maintain |
Exit |
Replace with another module or remove |
Rewrite or keep |
Check before adopting a contributed project: the project page’s supported Drupal versions, its security-coverage badge,
the date of its last release, its open issue queue, and whether it is the replacement for a module that was removed from
core (see Removed Core Modules). Prefer a small contributed module and a small custom module over a large custom module that
reimplements it. Whatever you write, follow Module Anatomy and the
coding standards. Add Composer packages with
composer require drupal/<project>, never by uploading code: module and theme upload from the UI was removed in 11.0
and 11.1.
Content entity versus node bundle versus custom table
Three places can hold the museum’s data.
| Criterion | Node bundle (content type) | Custom content entity | Custom database table |
|---|---|---|---|
Created by |
An administrator, in the UI, exported as configuration |
A developer, with an entity type class and a schema |
A developer, with |
Gets for free |
Fields, forms, Views, revisions, translations, moderation, permissions, search, JSON:API |
The same, once the entity type enables the features you need |
Nothing; you write queries, forms and access yourself |
Best for |
Editorial content that needs the full CMS (exhibition, event, news) |
A domain model with its own lifecycle and many records (the collection of objects) |
Logs, counters, imports, data with no editorial or field behaviour |
Cost |
Low |
A class, annotations or attributes, routes, permissions, handlers |
High if the data later needs the entity features |
The museum models exhibitions and events as node bundles, because editors, Content Moderation, Views and the front end
expect them. It models the collection as a custom content entity, collection_object, with fields such as
accession_number and period, because thousands of objects have their own list, access rules and import from a
collection-management system, and do not belong in the editorial content list. A table of sync timestamps for that
import is a custom table or state, since nobody edits it.
Rule of thumb. Start with a node bundle. Move to a custom entity when you need your own permissions, routes, storage or
bulk lifecycle. Use a custom table only for data that will never need fields, translations or Views. See
Content Types and Fields,
Custom Entity Types and
Database API and Entity Queries. Entity queries must call
accessCheck() explicitly, whichever of the first two you choose.
JSON:API versus REST versus GraphQL
All three expose content to other programs. JSON:API is part of core and needs no configuration; the core REST module (with Serialization) is also in core and exposes resources you enable one by one; GraphQL (5.x) is a contributed module. Simple OAuth (6.x) is contributed too, and 6 removed the implicit and password grants.
| Criterion | JSON:API | REST | GraphQL |
|---|---|---|---|
Where |
Core |
Core |
Contrib, the GraphQL project |
Shape |
Resources per entity type and bundle, with filtering, sorting, paging and includes in the query string |
Resources you enable and configure, one endpoint each |
One endpoint, a schema, and the client chooses the fields |
Setup |
Enable the module; it is zero-configuration and read-only mode is available |
Enable and configure resources, formats and authentication |
Build and maintain a schema and resolvers |
Writes |
POST, PATCH and DELETE on the same resources |
Per-resource methods |
Mutations you define |
Caching |
Cacheable GET responses with cache metadata |
Cacheable GET |
Needs more design to cache well |
Best for |
Headless front ends over content entities |
A few custom endpoints and legacy integrations |
Clients that need precise, nested, cross-entity data |
JSON:API follows the standard format with Drupal’s conventions, and business operations such as account creation, login and password requests are outside its scope. Because it exposes entities directly, bugs in the Entity, Field or Typed Data APIs can become security problems, so keep Drupal updated and use read-only mode where the front end only reads.
Rule of thumb. Default to JSON:API for the kiosk, because it needs no code and the museum’s gallery pages are read-mostly. Reach for REST only for a hand-shaped endpoint or an existing consumer. Add GraphQL when the client team needs to ask for deeply nested data in one round trip and agrees to maintain a schema. See JSON:API, REST and Serialization and GraphQL.
Starterkit theme versus subtheme
Since Drupal 10.0, Starterkit (php core/scripts/drupal generate-theme) is the way to start a theme: it copies a
snapshot of the starter theme into your project, and it stays yours. A subtheme declares a base theme and inherits
templates and libraries, which are updated when the base theme updates. Classy and Stable were removed in 10.0, and
Stable 9 was deprecated in 11.4. Olivero, Claro and Stable 9 leave core in Drupal 12, and Default Admin, based on Gin,
becomes the admin theme, so a theme that extends one of them as a base is the riskiest choice.
| Criterion | Starterkit-generated theme | Subtheme of a base theme |
|---|---|---|
Relationship |
A copy with no ongoing link |
A live dependency on the base |
Upgrades |
You carry the code yourself |
Base-theme changes arrive, sometimes breaking |
Control |
Total |
Limited by what the base allows |
Risk in Drupal 12 |
Low |
High if the base is Olivero, Claro or Stable 9 |
Best for |
A bespoke site theme such as |
A deliberate extension of a maintained contributed base theme |
Rule of thumb. Generate a Starterkit theme for a custom site theme. Use a subtheme only when you intend to track a maintained base and accept its updates. See Starterkit and Subthemes.
Config Split versus settings overrides
Configuration management exports the site’s configuration to YAML. Environments still differ (a dev-only module, a different email address, a disabled cache), and there are two ways to handle that.
-
Config Split is a contributed module. It defines splits, sets of configuration written to their own directories on export and merged with the main sync directory on import. Version 2.x, whose latest release at this check is 2.0.2 from February 2025, supports Drupal 8.8 through 11 and can split individual keys using a config patch. It filters the import and export pipeline and does not change active configuration. The splits that are active are chosen in
settings.php. -
Settings overrides are values in
settings.php(the$configarray) that override configuration at runtime without changing the stored configuration. They are not exported, which suits secrets and per-environment values and does not suit structural differences such as a module being enabled in one environment.
| Criterion | Config Split | Settings overrides |
|---|---|---|
Use for |
Different module sets, different config objects, a dev-only configuration |
Single values per environment, secrets from environment variables |
Visible in the UI |
Yes; the stored value is shown |
The form shows the stored value while the override applies, which confuses editors |
Exported |
Yes, in the split directory |
No |
Secrets |
Never put secrets in exported configuration |
Read them from the environment |
Cost |
A contributed module and discipline about what goes where |
None |
// settings.php
$settings['config_sync_directory'] = '../config/sync';
// A value that differs per environment and must not be exported.
$config['system.mail']['interface']['default'] = 'test_mail_collector';
// Activate a split for this environment (the split is created with Config Split).
$config['config_split.config_split.dev']['status'] = (getenv('MERIDIAN_ENV') === 'dev');
Source: Config Split project page
Rule of thumb. Use settings overrides for a few scalar values and for anything secret, and use Config Split when whole objects or module lists differ. Do not use both to control the same value. See Configuration Management and settings.php and Environments.
Single site versus multisite
Drupal can serve several sites from one codebase, each with its own sites/<name> directory, settings and database
(see Multisite).
| Criterion | Separate installs | One multisite codebase |
|---|---|---|
Isolation |
Strong: separate code, database, deploys, failures |
Shared code and PHP process; separate databases and files |
Updates |
One site at a time |
One code update affects every site |
Customisation |
Free per site |
Shared modules and themes; per-site settings only |
Operations |
More servers or containers to manage |
Fewer codebases, trickier deploys and configuration |
Tooling caveat |
Fully supported |
Automatic Updates does not support multisite |
Rule of thumb. Choose separate installs, usually with containers (see Docker and Containers), unless the sites truly share code, release cadence and a team, such as a museum with a main site and satellite sites that differ only in theme and content. Multilingual needs, such as the museum’s English and Spanish site, are not a reason for multisite: use one site with translations, see Multilingual.
Traditional versus decoupled
A traditional Drupal site renders pages with Twig and the render system. A decoupled (headless) site uses Drupal as a content and API back end, and a separate front end, for example Next.js, renders the pages. Progressive decoupling sits between: Drupal renders the page and a JavaScript application owns a part of it.
| Criterion | Traditional | Decoupled |
|---|---|---|
Team |
Drupal developers and themers |
Back-end and front-end teams |
Editing |
In-context editing, Layout Builder or Canvas, preview, contextual links |
Preview must be built; page builders do not render the external front end |
Caching |
Render cache, dynamic page cache, BigPipe, with cache metadata built in |
A CDN and the front end’s cache, invalidated from Drupal |
Security |
One application, the usual hardening |
Two applications, CORS, tokens and OAuth to design |
Cost |
Lower |
Higher: two codebases, hosting and deployments |
Best for |
Editor-driven sites |
Multiple channels, app-like experiences, a front-end team that wants its own stack |
Rule of thumb. Stay traditional unless you need more than one front end or the front-end team will not work in Twig. The museum keeps its public site traditional and uses a decoupled Next.js app only for the gallery kiosk. See Decoupled Drupal with Next.js and Authentication and OAuth.
Decision tree
When Drupal is not the right tool
Drupal pays back its learning curve when a site has structured content, roles, workflows, multilingual content and a long life. It is a poor fit in these cases.
-
A small brochure site with a handful of pages, edited by one person, and no custom data. The cost of Composer, updates, hosting and a developer is out of proportion. Look at the WordPress or Webflow sub-sections, which also compare their own strengths; the CMS Comparison has the cross-CMS metrics.
-
A complex application where the work is business logic, not content: transactions, dashboards, workflows without editors. Use an application framework; see the PHP and Laravel section. Drupal can still serve the content part of such a system through an API.
-
A shop as the main product. Shop extensions for Drupal exist, but selling is a different problem; compare dedicated platforms first in the E-commerce comparison.
-
No technical skills or hosting to keep Composer-based updates and security releases going. Drupal expects an administrator who can apply security releases; Automatic Updates covers patch-level core updates only.
-
Very short projects where the time to learn the platform exceeds the time to build the site.
Common mistakes
-
Building a custom entity for editorial content that a node bundle would handle, and then re-implementing revisions and moderation.
-
Using a contributed module without checking its Drupal 11 support or security coverage.
-
Overriding the same setting in Config Split and in
settings.php, so nobody can tell which value applies. -
Choosing a base theme that leaves core in Drupal 12.
-
Going headless for a site with a single front end, and then rebuilding preview, forms and caching by hand.
-
Choosing multisite to get translations.