Who this is for. Anyone who has scored a project on The decision table and wants to see the same choices made on real sites. After this page you can point to a production site shaped like your project, and say what it would do differently.

Each study is taken from a live site's repository, not from memory, and each site is described by what it is rather than by name. The first finding is that none of them is headless. Every one renders its pages on the server, and the most API-driven parts are islands inside server-rendered pages.

The five at a glance

StudyShapeEngineWhy it fits
Touring and ticketing with a member areaHybridSmartyMost visitors see the same cached pages; members get a few per-visitor elements as fragments.
Online courses with commerceHybridSmartyThe domain lives in custom entities and jobs; the checkout and everything per-visitor run in the browser.
A large content siteServer-sideSmartyEditors compose pages from a component library, and almost nothing on a page is per-visitor.
A Twig landing-page platformServer-side, with form islandsTwigTwelve hostnames share one template set; brand and campaign differences come from content.
HeadlessNone of the sites–The nearest are islands inside server-rendered pages.

Touring and ticketing with a member area

Shape: hybrid, server-rendered Smarty, two brands on one site.

  • There is no cart. Tickets link out to ticket sellers, so the site carries no commerce of its own.
  • Two brands share one site as two environments of it, and the templates tell the brands apart by the environment a request arrives on.
  • Members get presale codes and a member top bar as fragments, on pages that are otherwise cached.
  • Personalisation is a separate bundle, loaded only when an admin switches it on in site data. It records recent visits and the visitor's location in the browser, then requests server-rendered recommendations with that profile in the query string.

The caching consequence of the member area

A member area on a cached site is the case What server-side rendering costs describes: one personal element inside a stored page shows one member's data to the next visitor. This site keeps its pages cacheable instead. The member pieces are fragments that switch caching off (preventCaching()), so only they are rendered on every view. Pages are stored per hostname and path, so the concert detail page names the two query parameters its output depends on, the presale code and the concert, with setCacheVaryParameters(), and each combination is stored as a page of its own.

What it shows: personalisation does not require giving up the page cache, when the personal part is a fragment.

What to copy: decide, page by page, which query parameters change the output and declare them, and switch caching off only on the fragments that are personal. The switchable bundle is the pattern on A personalisation bundle that loads only when it is on.

Online courses with commerce

Shape: hybrid, server-rendered Smarty.

  • The domain is modelled in the platform, not in code. Courses and enrolments are custom entities, and the Books module is the lesson player.
  • Behaviour lives in jobs, the platform's event-driven automation.
  • The checkout is a single page that talks JSON to the checkout API. Its state is handed over from the server in a window object, and every request goes through one API client that sends the CSRF token in an X-Csrf-Token header.
  • Everything per-visitor is a fragment, served by name through one dispatcher: the header's account and cart links, the purchase button on a lesson, and the analytics context.

What it shows: a site builder can deliver an application, with enrolments and payments, from custom entities, jobs and templates, without framework code. It is the strongest evidence in this guide for that claim.

What to copy: the checkout is the documented hybrid shape. State is handed to the browser once (Handing state to the browser), and every call goes through one module (One client module, carrying the CSRF token).

A large content site with a component library

Shape: server-side, Smarty.

  • 77 component templates, composed by editors into pages.
  • Very little JavaScript state. No state is handed to the browser in a window object, and layout area values are mapped straight onto CSS classes.

What it shows: a content-led site needs none of the hybrid machinery until it has per-visitor content.

What it would do differently: one of its button components prints its link with the area shorthand inside an attribute, href="{$component->url}". The shorthand reads a text area through getAreaHtml(), which wraps the text in a live-preview <span> while an author edits in place, so in live preview that attribute holds markup rather than a URL. Two sibling components in the same folder do it the recommended way: one prints a record's own fullPath, a property of the record rather than a layout area, and the other reads its layout area with getAreaText(), which wraps only rich text. The rule: in an attribute, a script or JSON, read text with getAreaText().

A Twig landing-page platform

Shape: server-side, Twig, twelve hostnames from one template set.

  • Twig throughout. The engine is one setting for the whole site (Switching template engine).
  • Brand by hostname. Twelve hostnames share the templates. Brand-specific pieces live in a folder per brand, reached through a macro, and brand copy and prices come from global components named after the hostname.
  • A landing-page system whose page-level colours are custom properties set from the record, so each campaign sets its own colours.
  • A modern front-end build: fifteen webpack entries, almost all TypeScript.
  • Forms post to the customer's own back end, not to Core dna, with the endpoints handed to the browser in a window object.

What it shows: Twig is a peer choice for a large, design-led site, and one template set can carry many brands.

What it would do differently: the per-brand include names a single file, so a brand that lacks that file fails the page. The list form of include(), naming the brand's file first and a shared one after it, gives every brand a fallback. And the site's own architecture notes list ten of its fifteen webpack entries: notes kept beside the code drift unless someone checks them at release time.

Headless: no site, and the islands that come closest

No site studied for this guide is headless, which is itself the finding. The nearest are two islands:

  • the course provider's single-page checkout, over the checkout API;
  • the Twig platform's forms, which post to the customer's own back end.

Each is an island inside a server-rendered site, which is the hybrid shape. Neither site rebuilt routing, preview or cache invalidation, because the platform still composes the page around the island.

If your project is headless, the study to write is your own. What a headless build must rebuild is the worksheet for it, and Drafts, preview and knowing when content changed covers preview and invalidation in detail.

Lessons that recur

  • Per-visitor data on a cached page belongs in a fragment. Both hybrid sites do this for everything personal: account links, the cart, the purchase button, presale codes (The three things that always need a fragment).
  • Declare what a cached page varies by. A page whose output depends on a query parameter names that parameter, as the touring site's concert page does.
  • Model the domain before writing code. Custom entities and jobs carry a whole course business.
  • In an attribute, read text with getAreaText(). The shorthand is for printing into the page.
  • Keep the architecture notes next to the code, and check them at release time. One repository's written notes had drifted from the code they describe.