Choosing Content Ownership
Decide which CMS object owns each kind of content before writing templates: Blog or datastore, who owns a landing page, pageless datastores, picks versus a fetch, and where fields live.
Before writing templates, decide which CMS object owns each kind of content in the design. This is a different question from Choosing a Content Architecture, which decides how a template reads its data (page fields or queries). Ownership comes first, because it decides what datastores, templates, and menus exist at all.
Most designs can be built several valid ways, and few rules fit every site. What follows are considerations and what each choice does in the CMS. Weigh them against the site in front of you and write the reason for your choice down.
Default choices
• Dated posts with a landing index and tags or authors (news, a journal, "the blog"): a Blog with BlogPosts, not a datastore. See Blog or datastore below.
• Two or more cards that lead to similarly structured detail pages: a datastore with its own page per item (CreatePageByDefault, a DefaultTemplateGUID for the detail template, a DefaultRootPath for the URLs). One listing template renders the family; one detail template renders each item. A datastore's own fields should cover what the item needs wherever it is shown: the card on a listing, a picker on another page, the detail page, any template that reads it. Image, eyebrow, and short description are typical. The detail page's body can belong to the partials the detail template includes, so editing the overview means editing one partial, not every item; Where the fields live, below, says what that placement assumes.
• The same group of cards shown in several places: one datastore, with each page selecting the cards it shows through a datastore item or datastore item list field rather than copying card content.
• Two families with the same card and detail shape but different names ("Services" and "Solutions"): one datastore plus a discriminator field (a select or radio such as offering_kind) that listings and pickers filter on. Split them into separate datastores only when they need different fields or have different owners or lifecycles — not to cut the datastore count, and not because the design shows them in two sections.
• Repeated content that never has its own page (FAQs, partner logos, testimonials, videos): a datastore without pages, one item per entry. Pages select items into view with a datastore item list field instead of storing the text again. FAQs grouped by category are a page fieldset of categories, each with its own list of selected questions, so one question can appear under several categories. Such a datastore can still own a landing page; see Pages without page items.
• Site-wide content (logo, contact details, call to action, social links, footer groups, chosen menus): one settings datastore with a single item that the header and footer read.
• A link to something already in the site: the target's own URL (for example item.full_url), never a copied path.
• A genuinely unique page (legal, a campaign, a one-off editorial page): an Article with its own template and fields. Record why it is an exception.
These are starting points. Choose differently when a page really is one-off or an existing site already uses another model, and write the reason down.
Blog or datastore
A blog should almost always be a Blog with BlogPosts rather than a datastore whose items have pages. This is a strong consideration, not a prohibition, and the reason is what each object already provides:
• A BlogPost has a post date, summary, body, featured image, tags, and authors built in, and the Blog is itself the page at its URL. A datastore item has only the fields you define, so a datastore blog redefines all of those, then needs a listing template that rebuilds what {% blog_posts %} already does: filter by blog, tag, author, and a date range, and order by post_date.
• Blog, BlogPost, and {% blog_posts %} are designed together, and the blog scenarios show the whole set; a datastore replaces them with templates you maintain.
A datastore is the better home for records that are not posts (locations, job openings, products), even when a listing orders them by date. If you choose a datastore for post-like content anyway, record why.
The /blog menu item should lead to the Blog itself, which is the landing page, not to a separate listing Article. Its Href can reference the Blog as an entity or carry the Blog's URL as a plain path; see URL Field Values vs Menu Href. To build the pages, see the blog_posts method, the topics Obtain a Collection and Blog Post, and the scenarios blog-main and blogpost-main (fetch-scenario.js).
Who owns a family's landing page
The listing at a family's root (for example /equipment) can belong to the datastore or to an Article. Both work; they trade off differently.
• The datastore owns the landing. Give the datastore record its own URL and listing template alongside the settings its items use:
{
"Name": "Equipment",
"Title": "Equipment",
"DatastoreType": "equipment",
"CreatePageByDefault": true,
"URL": "//www.example.com/equipment",
"TemplateGUID": "/pages/equipment_listing.liquid",
"BrowserTitle": "Equipment",
"DefaultTemplateGUID": "/pages/equipment_item.liquid",
"DefaultRootPath": "/equipment/",
"EnforceRootPath": true,
"$ObjectType": "Datastore"
}
URL and TemplateGUID make the datastore itself a page rendered by the listing template; like any page it needs a BrowserTitle. DefaultTemplateGUID and DefaultRootPath pre-fill the template and URL an editor would otherwise pick for each new item. EnforceRootPath only has an effect when DefaultRootPath is set: when true, the CMS rejects an item whose URL does not start with that path ("URL must start with ..."); when false, the path is only a suggestion. Turn it on when the whole family should live under one root; leave it off when some items need to live elsewhere.
Consequences: one record holds the family's schema and its landing, and nothing else has to be kept in step with it. The CMS requires each URL to be unique per domain across all pages, so an Article cannot also claim /equipment, though an Article at another path is fine. The listing template's own custom fields (an intro, a banner, a call to action) are saved on the datastore record, so presentation fields alone are not a reason to use an Article. A plain every-published-item listing needs no extra field.
• An Article owns the landing. The Article takes the URL and a page template. It can select and order the family through a picker field, or fetch it. Consequences: the editor controls which cards appear and in what order for this landing; the Article can mix items from several datastores or other content records; it is a second record that describes the family, so a new item appears only if the Article picks or fetches it. Choose this when the landing is hand-curated, mixes sources, or wants a different editing surface than the datastore.
Either way, items may name their own TemplateGUID, so one family can mix page types. If both a datastore and an Article describe the same family, write down which one is the landing and why.
Pages without page items
CreatePageByDefault only sets whether the editor's "give this item its own page" option starts checked (see CreatePageByDefault). It does not decide whether the datastore itself can be a page. A Testimonials datastore with CreatePageByDefault false can still carry a URL and listing TemplateGUID, so /testimonials is a page while no testimonial gets a URL of its own. The same items can be picked onto Home or any other page with a datastore item list field, so the text lives in one place. FAQs and calls to action work the same way: pages pick them, and a menu route can land on the datastore.
A block that appears in more than one place (testimonials on Home and on /testimonials, one call to action on many pages) should name its source: the datastore and the picker field that selects from it, for example Home's featured_testimonials field, a datastore item list over Testimonials. Keeping a second copy of the text in a settings record or in a section's own fields is also valid; the cost is that the copies drift apart.
Picks, a fetch, or both
How a listing is populated is a separate choice from who owns its route.
• A picker field fits an editor-curated listing and its order. entitylist selects any content record; datastoreitemlist selects from one datastore. Set HasUrl on an entitylist field when every pick must have its own page: the CMS then offers only entities with a URL and rejects a saved value without one. When the list may mix entities with and without pages, leave the filter off and guard each link with item.has_url.
• A fetch ({% datastore_items %} and the other list methods) is not only "show everything". It can return the most recent items, a filtered or queried subset, or random items, ordered with sort_by (a custom field's FieldID works on datastore_items) or with | sort:. List methods return 10 items unless limit says otherwise (Obtain a Collection).
• They combine well: show the editor's picks first and fill up to a maximum with recent items, or fetch random items when nothing was picked.
Field on the page template (Type datastoreitemlist, DatastoreGUID Services, FieldID featured_services), then:
{%- var max_cards = 6 -%}
{%- var cards = entity.featured_services -%}
{%- if cards.is_valid -%}
{%- datastore_items cards = datastore:'Services' prepend:cards unique:true limit:max_cards max_size:max_cards -%}
{%- else -%}
{%- datastore_items cards = datastore:'Services' sort_by:'random' cache_random:true limit:max_cards -%}
{%- endif -%}
{%- for card in cards -%}
{%- if card.has_url -%}
<a href="{{ card.full_url }}">{{ card.title }}</a>
{%- else -%}
{{ card.title }}
{%- endif -%}
{%- endfor -%}
prepend puts the picks first; unique:true keeps them out of the fetched results; max_size caps the total, lowering the fetch to what is left after the picks. Without an argument such as limit, a call made of prepend alone fetches nothing.
Where the fields live
A datastore item's fields can live in two places, and each assumes something.
• On the Datastore (its own Fields). Every item has them, whether or not it has a page, and wherever it is shown. A datastore whose items never have pages (CreatePageByDefault false, no URLs) needs its fields here: values defined by a template are saved only for an item that has a page and a template, so a field defined on a partial is not saved for a pageless item.
• On a partial's fields file. The template that reads a FieldID defines it (see Where Custom Fields Belong), and the field is composed onto the form of every item whose page includes that partial. This works well when each item has a page; it assumes it does.
When several datastores render through one partial (one card partial for Equipment, Materials, and Industries), the partial reads fixed FieldIDs, so each datastore must define them. Declaring the shared FieldIDs once and reusing that same set on every datastore avoids drift; redeclaring them by hand per datastore invites a field missing on one, whose cards then render blank. Family-specific copy can differ and live on that family's detail partial.
Loading the settings record once
Load the settings datastore by name, near the top of the shared header, and give every partial the one variable:
{% datastore_items var site_settings = datastore:'Site Settings' limit:1 %}
{% assign settings = site_settings.fetched[0].data %}
Partials then read settings.company_phone and the like. Loading by name rather than GUID keeps the code working on every site the template is copied to.
Putting it together
A typical catalog site combines these: a settings datastore read by the header and footer, one or more menus, a page-owning datastore for each card family with a listing template and a detail template, and thin Articles for one-off pages whose partials each own their fields. CreatePageByDefault explains what that property does and does not do; every page-owning item still needs its own URL, template, and browser title.