Skip to Content
ConceptsFor Drupal Developers

Concepts for Drupal Developers

This page is direct about where the analogy in Concepts holds up for a Drupal background and where it doesn’t: methodology, not just terminology. Everything linked below is confirmed elsewhere in this documentation; this page is the Drupal-shaped index into it.

Terminology, fuller than the quick table

DrupalP1Where it actually differs
A Drupal site (one settings.php, one database)A SiteOne Site pairs with one Next.js codebase and one CCR content backend. See Sites, branches, and workstreams.
NodeDocumentSame idea, different storage: a document is a CCR object at a path, not an entity in Drupal’s entity/field system. See Workflows.
Content moderation state (Draft/Published/Archived)Autosave → Publish → CheckpointNot a moderation state field on the entity. Publish is an explicit API call that creates a checkpoint; the public read path only ever serves checkpoints. See Publishing.
RevisionsVersionsEvery autosave creates one, same spirit as Drupal’s revision system. See Publishing.
(no built-in trash for nodes)Soft delete + restore()P1 has what Drupal core doesn’t: a real undo for a deleted document. See Publishing.
Config sync (config/sync/*.yml) / a staging environmentBranch (Workstream) + real mergeA true content fork/merge, not a config-export/import cycle. See Branches. Note the scope difference below.
Content Type + Field UITemplateMetadata (name, label, URL pattern) is scriptable; the field/layout structure is not: it’s built by hand in the Puck editor, every time, with no config-as-code export. See Templates.
Paragraph type / Block pluginPuck componentA plain JS/TS object, not a plugin class with annotations and a Twig template. See Components: Puck’s answer to blocks.
Field (Field UI / Field API)Puck fieldDeclared inline on the component, not a separately-managed, reusable field storage/instance pair. See Fields Reference.
ViewsDynamic (templated) routesDifferent mechanism for the same job. See below.

Content modeling: Content Type + Field API → Template

Drupal’s Field API is powerful and reusable: a field can be defined once and attached to multiple content types, with its own storage settings independent of any one bundle. P1’s Template has no equivalent reusability: Template creation is metadata-only (name, label, description, URL pattern, scriptable via the Templates API), but the actual field/component layout has no write API at all, confirmed directly from CreateTemplateParams’ own type comment: “Metadata only; the layout is authored on the editor canvas afterwards.” There’s no Drupal-style config-as-code YAML export for a Template’s structure, and no cross-Template shared-field mechanism the way one field storage can back several content types in Drupal. Every Template’s layout is built by hand, once, in the Puck editor. See Templates, and Products Template / Cocktails Template for what that looked like on a real brand site.

What P1 has that core Drupal doesn’t ship for free: changing a Template’s structure after real content already exists against it comes with dry-run previews and one-call rollback, self-serve, closer to what Drupal’s Update API offers a developer than to anything a content team could do themselves. See Templates.

Publishing: content moderation → autosave/Publish/Checkpoint

Drupal’s content moderation module gives you an explicit state machine on the entity (Draft → Needs Review → Published, or whatever your site defines) and you can see that state on the node. P1’s equivalent has no visible status field on the document at all: it’s autosave-as-draft-version, with an explicit Publish action that creates a checkpoint the public site actually reads from. The state exists, it’s just not surfaced as a field the way Drupal’s moderation state is. Full mechanics, confirmed from the SDK source: Publishing.

Staging & environments: config sync vs. content branches, a real scope difference

This is the comparison worth being precise about, because the two mechanisms solve different problems. Drupal’s config sync is about configuration (content types, views, field definitions, permissions), moving between environments as YAML, deliberately excluding content. P1’s branch/merge is about content: a Workstream forks actual document content and merges it back, the way a Git branch would. There’s no P1 equivalent to Drupal’s config-export/import workflow (Templates have no config-as-code path at all, per above), and no Drupal equivalent to branching content itself the way P1 does: Drupal’s content moderation states live in one tree, not a forkable one. See Branches for the real mergeability-check/preview/execute API, and the merge-request review flow (closer to a GitHub PR than anything native to Drupal).

Multi-site has a shape mismatch, not a direct equivalent. Drupal Multisite (one codebase, several independent databases/configs sharing the same code) is closer in spirit to running several separate P1 Sites than to anything within one Site, but confirmed directly from SitesEndpoint (only get()/list(), no create/duplicate), P1 has no API to stand up or clone a new Site’s content. If your team has used Drupal Multisite to give different markets or business units independent content ownership, read Localization: it addresses this exact question and lays out what building the equivalent on P1 would actually take.

Paragraphs/blocks → components: no annotations, no Twig

A Drupal Paragraph type or block plugin is a PHP class with an annotation, a schema definition, and a Twig template rendering it. A Puck component is one plain object: label, fields, defaultProps, render (real JSX, not a template language), imported into puck.config.tsx. No annotation discovery, no .info.yml, no separate template file to keep in sync with the class; the object is the registration and the render logic together. See Components: Puck’s answer to blocks for the shape, and Components Reference for what ships out of the box.

Views → dynamic routes: no query builder

Drupal’s Views gives you a UI to build a listing/filtering query and choose a display (page, block, feed). P1’s answer to “one template, many URLs” is a dynamic route: a template with a colon-segment path pattern like /company/leadership/:name, matched and resolved at request time. It’s a routing mechanism, not a query-builder UI: there’s no Views-style exposed-filters/sort UI, no visual query configuration. If a page needs Views-like filtering or listing logic, that’s application code against the CCR API, not a dashboard feature. See Dynamic (templated) routes.

Things Drupal gives you that P1 doesn’t

Confirmed absent, each with the plan for filling it:

  • Menu UI + block layout (Drupal’s Menu module, Block Layout): nothing built in for site-wide navigation/chrome. See Managing the Header & Footer.
  • A form builder (Webform): nothing native to P1 or Puck, and no equivalent to Webform’s built-in submission storage. See Form Builder and Form Builder → Submission Storage.
  • SMTP/transactional email (SMTP module, Symfony Mailer): P1 has no email-sending capability at all; it’s genuinely new infrastructure to add. See Email Delivery.
  • Metatag + XML sitemap + Structured Data modules: P1’s per-page SEO/OG fields are real and built in, but sitemap.xml, robots.txt, and JSON-LD structured data aren’t generated automatically the way those modules give you on install. See SEO & GEO.
  • Image styles with automatic responsive image derivatives: Drupal core’s Image Style + Responsive Image modules generate a srcset for you. Nothing in @pantheon-systems/p1-media’s exported surface produces a srcset automatically; each component controls exactly one delivered width/height/format per call. See Media Library.
  • A contrib module ecosystem: there’s no P1-equivalent marketplace of install-and-configure extensions. Extending a site means installing an npm package (like p1-media) or building a component yourself.

Habits to unlearn

  • No config sync YAML. Nothing about a Template’s structure round-trips as exportable config the way a Drupal content type’s fields do. See Can a template’s layout be defined in code?.
  • No entity/bundle/field storage separation. A Puck field is declared directly on the component that uses it: there’s no equivalent to Drupal’s reusable field storage shared across bundles.
  • No Twig. Rendering is JSX inside the component’s render function, not a template file resolved through a theme’s suggestion hierarchy.
  • A real but much coarser permissions system than Drupal’s. P1 has four content roles (admin/editor/author/junior-editor) controlling structural editing rights in the Puck canvas: nothing close to Drupal’s granular permission-per-entity-operation model, and no confirmed per-entity or per-bundle access control. See Permissions & Roles.
Last updated on