Content model
Evolve has one content model for Storyblok, Contentful, and Payload. The three CMS packages serve the same GraphQL types from the same content types, with the same ids and field names, so a storefront does not depend on which CMS a project uses. The template owns the page heading, the breadcrumbs, and SEO; the commerce engine owns the product listing; the CMS owns everything in between.
How a URL reaches a page is in Pages and routes. How to add a block of your own is in Add a content block.
Page types
Section titled “Page types”A page type is decided by what owns the main content of the page: a list of
blocks, the product listing, a text, or a list of articles. Every page type is
its own content type in the CMS, and its own GraphQL type that implements the
Page interface. A route that does not know which type lives on a path asks
for page(path) and selects the fields of Page.
| Page type | For |
|---|---|
ContentPage |
Home, campaigns, brand and service pages |
CatalogPage |
A category page, created when the engine creates the category. As a collection, it is a curated part of a category, such as one brand, with facet filters as its source. Its layout is listing, or landing with subcategory tiles. |
ArticlePage |
Blog posts, buying guides, news; its parent overview is its section |
ArticleOverviewPage |
One overview per section of articles, with an indexable page per topic below it |
Every page type has the Page fields: heading, path, translatedPaths,
breadcrumbs, and meta. Topics and authors are entries of their own, which
articles refer to. Product.articles returns the pages and articles about a
product’s category, for the product page.
Fields and editing
Section titled “Fields and editing”| Part | What it does |
|---|---|
| Ids and names | Content type ids are in snake_case and the same in every CMS, lists are named *_list, and GraphQL names are in PascalCase, such as article_list and ArticleList. |
| Site settings | SiteSettings: which page answers each recurring purpose, such as contact, returns, and shipping, one entry per store. |
| SEO fields | Three plain fields on every page, seoTitle, seoDescription, and seoImage, without an SEO plugin. An empty field falls back to the site’s template. |
| Lead image | leadImage below the heading. It is also the page’s card image and its og:image. |
| Field tabs | Every field sits on the Content, SEO, or Settings tab, the same in every CMS. |
| Key fields | A product SKU or category key is a text field checked against a pattern you set per project, because key formats differ per engine. |
| Eject | evolve --package @evolve-framework/<cms> eject <block> copies a block into your project, so you can change it. |
Field tabs map to each CMS’s own feature: Storyblok tab fields, Contentful editor layout tabs, and Payload’s unnamed tabs, which keep the stored data flat.
Rich text
Section titled “Rich text”Rich text is a tree that does not depend on the CMS. Each package converts its CMS’s format into it, and one renderer in the storefront draws it.
type RichText { document: JSON! # the tree embeds: [RichTextEmbed!]! # what the tree embeds, by id links: [RichTextLink!]! # internal links, resolved to a path in the current locale}- The tree has paragraphs, headings from level 2, lists, quotes, links, and tables. The page heading is the H1, so headings in rich text start at level 2.
- The package resolves an internal link to the target’s path in the current locale.
- An embed is content placed in the text, returned as a typed GraphQL object, such as a product with its live price.
Blocks
Section titled “Blocks”A block has one job. Layout follows from the content, such as the number of items or whether there is an image; there are no color, width, or alignment fields, because those belong to the theme. Each place on a page accepts its own group of blocks, and every CMS enforces the group with its own validation.
| Block | What it does | Where it goes |
|---|---|---|
campaign_banner |
An image with a focal point, text beside it, one call to action, and an optional end date. Its height is capped. | Content pages, the top of a catalog page |
content_teaser |
An image beside text, with up to two links | Content and catalog pages |
promo_list |
Two to four promo cards that link to a page, a product, or a URL | Content, catalog, article, and overview pages |
category_list |
Picked categories, or the children of one category | Content and catalog pages |
product_list |
Picked SKUs, or a product source. Sold-out products are left out. | Content, catalog, article, and overview pages |
article_list |
Picked articles, or the newest by section, topic, or category | Content, catalog, article, and overview pages |
brand_list |
Brand logos that link to their brand pages | Content and catalog pages |
usp_list |
Selling points | Content and catalog pages, columns |
rich_text |
Formatted text with anchors on its headings and typed embeds | Every page type, columns |
faq |
Questions and answers, with FAQPage structured data |
Every page type, columns |
table_of_contents |
Links to the headings on the page, generated | Content pages and articles |
columns |
Two to four blocks side by side, stacked on a phone. A project opts in. | Content pages |
image |
One image with a caption | Content pages and articles, in rich text, columns |
video |
A YouTube video, shown after consent | Content pages and articles, in rich text, columns |
tip |
A highlighted tip or warning | Articles, in rich text |
product |
One product with its price and buy button | In rich text |
promo |
One promo card | In rich text |
external_component |
A marker for a component in your code, such as a newsletter form | Content, catalog, and overview pages, columns |
snippet |
Places blocks that you edit once and use on many pages | Content, catalog, article, and overview pages |
There is no hero block and no page title block: the template renders the
heading, leadImage shows below it, and campaign_banner takes the campaign
part. There is no divider either, because spacing belongs to the theme.
Fixed system copy, such as the text of an empty cart or of no search results,
is not a block. It lives in one messages entry per store, which nobody places
on a page.
A page stores a slug per locale and a parent page. Its path is the chain of
parents and its own slug, so a child moves with its parent, and the breadcrumbs
follow the same chain. translatedPaths gives the alternate-language links.
When a page moves, the old path redirects to the new one. Editors also keep a list of redirects of their own. Both are per market.
Product sources
Section titled “Product sources”A source says which products a block or a catalog page shows: a category,
filters in the storefront’s own facet keys, and a sort. product_list and
CatalogPage share it. An editor filters the shop, copies the query from the
URL, such as brand=head&sale=true, and pastes it into the CMS.
Every source is checked on publish and on a schedule, so a renamed facet is noticed before shoppers see an empty list.
Audience and markets
Section titled “Audience and markets”| Part | What it does |
|---|---|
| Audience | Shows a block to everyone, to signed-in shoppers, or to signed-out shoppers. It is applied in the browser, so the page stays cached for everyone. |
| Markets | Limits a page, a block, or a menu item to the countries a store sells in, such as a legal page for Germany. It is applied on the server. |
Audience decides what is relevant to a shopper, not what a shopper may see: every variant is in the page source. Content that must stay private needs a check on the server.
Caching
Section titled “Caching”Publishing a change sends an event with the changed paths, which the storefront revalidates. Cache tags per source go further: publishing an article also refreshes its overview, the pages that list it, and the product pages that show it.
How the storefront caches pages is in Caching.

