A page should not hard-code the result of “find the six newest articles” into its layout. Declare a content source first, then reference it in a layout's top-level props. A source is a small four-field contract: path, optional sort, optional limit, and optional filter.
Declare a source in page scope
A page can declare sources beneath pages/<page>/sources/. These files together define a source named related:
pages/topic/sources/
├── related # /content/articles/
├── related.sort # -date
├── related.limit # 6
└── related.filter # tags=AFS,Web Device
# category=ResearchThe content of related is a source path such as /content/articles/. .sort and .limit are optional. .filter has one condition per line: lines are ANDed together; comma-separated values on one line are ORed. The example means “tag contains AFS or Web Device, and category exactly equals Research.” Field matching is currently exact and case-sensitive; an item without the field does not match.
A source path must live below /content/<type>/. It can point to a subpath inside a collection to narrow the scope. Do not write .. or treat arbitrary filesystem paths as content sources. An invalid source path resolves to an empty result rather than reading outside the content root.
Bind it in a layout
A source name resolves within its page or collection scope. A layout references it as a complete placeholder value, for example:
items=$source.relatedor reads an available path from it when needed. Do not mix $source.related into ordinary prose, and do not assume it will resolve in an arbitrary nested object: current source-binding checks treat complete $source.<name> values in a section's own top-level props as declarative bindings.
The same source name can mean a different query on a different page; it is not a site-wide global variable. $source.related on the topic page should be answered by pages/topic/sources/related (or a source declaration on that collection record), not by an identically named file on another page.
Make empty results diagnosable
An undeclared $source.<name> used to make a page silently display an empty list. The current runtime can check whether a layout's required source is declared in the same scope and labels a problem source-binding-undeclared, including the names that scope did declare. Still inspect the browser result: a real source can also return an empty collection because of its path, filter, locale, or content.
Use this acceptance order:
- Start with an unfiltered
/content/<type>/source and verify that list and detail objects appear. - Add
sort, thenlimit, and add a filter last. - After each layer, run the DSL/site checks and a build, then inspect the real page.
- Give a component an explicit empty state for a legitimate zero-result case instead of hiding missing data.
For tags and relationships on content objects, see Content objects, metadata, and locales. For complete field boundaries, see Source-binding reference.