Dynamic Components

A dynamic component is a reusable block that renders live data from your site — for example "the latest three posts tagged news" or "every author, shown as a card." The server fetches and filters the data before rendering, and exposes the rows to your component as items. Once built, you can drop the component on any page and it always shows current content. This is the fastest way to give an agent free rein over your data.

The idea

Every component has a kind. A static component is just markup. A dynamic component additionally declares one or more sources — the collections it reads. When the page renders, the server queries each source (with any filter, sort, and limit you set), and the component's template loops over the result as items.

The live collections you can bind are the same ones available everywhere: posts, authors, categories, tags, and changelog. See Live Collections for what each one contains.

Build one with an agent

The flow is: discover the source, optionally preview the rows, create the component, place it, deploy.

  1. Discover the available sources and their fields — list_data_sources.

  2. Preview rows before committing (optional) — list_data_source_items with a source_id, filter, sort, and limit.

  3. Create the component as kind="dynamic". A dynamic component must declare three things together: a block_type (the shape, e.g. list), a js_runtime (use none for pure server-rendered Liquid), and a non-empty sources list. For example:

content_create_component
  slug: latest-news
  name: Latest News
  kind: dynamic
  block_type: list
  js_runtime: none
  sources: [{ source_id: "posts", default_filter: { tag: "news" }, default_sort: "-published_at", default_limit: 3 }]
  html_template: '{% for item in items %}<a href="/blog/{{ item.slug }}">{{ item.title }}</a>{% endfor %}'
  1. Place it on a page — page_insert_section with the component's slug. The server fetches the bound rows and renders them.

  2. Deploycontent_deploy_site.

items vs. the global collections

Inside a dynamic component, the rows from your declared, filtered binding arrive as items. The global collections ({{ posts }}, {{ authors }}, …) are also available in every component regardless — use those for a simple unfiltered list, and items for the component's own filtered binding.

Things to watch

  • All three are required. kind="dynamic" without a block_type, a js_runtime, and at least one source is rejected. Set them together.

  • Filters run on the server. The default_filter on a source (or the filter on list_data_source_items) is applied before rendering. A {% if %} in your template only hides rows that were already fetched — it does not narrow the query.

  • Some sources are singletons. The personalisation lead for a landing page is a single record, not a list — use it directly on a /lp/ page (see Dynamic Landing Pages), not as a list binding.

  • Directory data is coming. Binding country / city / street / business directory sources is not available yet; the blog collections (posts, authors, categories, tags, changelog) are live today.

Next steps