Skip to content

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.

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.

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 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.

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.

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.

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.

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.