Skip to content

Lists and their data

A list drawn by a loop is one layer with one item, and what the item reads from data (a title, a link, an image) shows as a variable, with a way to where the data is kept.

View as Markdown

Most pages have a part the code doesn’t write out item by item: a grid of products, the posts of a blog, the rows of a table. The code writes one item and a loop draws it once for each entry of something: a collection in your files, a table in a database, the products of a store.

src/app/page.tsx
<ul>
  {posts.map((post) => (
    <li key={post.slug}>
      <a href={`/blog/${post.slug}`}>{post.title}</a>
    </li>
  ))}
</ul>

midcode shows that the way the code has it. The list is one layer and its item is one thing, like an instance of a component: you select it whole, and the panel lists what it shows from the data as variables. The words that come from data aren’t something to type over on the page: they’re a field of the item, and they’re changed where the data is kept. To change how the item is built, you open it.

A list is one layer

In Layers, the element a loop repeats is a single row in purple, with a database icon, the name of what it goes over (“Posts”, “Products”) and how many items the page shows. It has nothing under it: the item isn’t taken apart until you open it.

On the canvas every item is there, as the site draws it. Click anywhere in one and the whole item is selected, outlined in purple, with the other items outlined faintly: they’re the same code.

The right panel opens with a “List” section:

  • the list’s name and which item this is (“Item 3 of 8”); a click opens its data;

  • the loop as written, with a button that opens it in the code;

  • the item’s variables: each text, link and image it shows that comes from the data. The field’s name is on the left, with the icon of its kind (“Title”, “URL”), and what this item shows for it is on the right, in violet (“Gift Card”, /products/gift-card). Hover the value for all of it and where it’s read from; click it to select the element that shows it. Where the connection can be changed from the panel, the value has a ✕ at its end and a right-click menu: see Connect something to a field;

  • “Edit item”, when the item is written in place.

When the item is a component, its variables are its props: the section named after the component lists each prop the loop gives it, with this item’s value in violet in place of a control (title={post.title} reads “Title” → “Gift Card”), and “Edit component” under them. A prop given a plain value keeps its control. This holds for any instance, in a list or not: a prop whose value is read from data shows what it has, and the field on hover.

Open the item

To change what’s inside the item (its layout, its styles, which elements it has), open it:

  • If the item is a component (<Card … /> in a .map()), “Edit component” opens it in component mode, on its own canvas.

  • If it’s written in place (an <li> in the loop, a Liquid snippet), “Edit item” opens it where it is: what’s inside can be selected and edited, and Layers lists it under the list’s row.

A double click on the item does the same, and lands on what was under the pointer. Selecting anything outside the item closes it again.

What you change in an item’s layout or style changes every item, because there is one item in the code. To make one item different, the code has to say so (a condition, a variant): ask your agent.

What the item reads from data

With the item open, select a text, a link or an image inside it. Where the code reads it from the item, the right panel shows one row in place of the control, the same row the item’s variables are: the field’s name on the left, what this item shows in a violet chip on the right. The section’s header says which list it’s from.

The code saysThe panel shows
{post.title}Text: “Title” → “Hello world”, from “Posts”
href={`/blog/${post.slug}`}Link: “Slug” → /blog/hello-world, from “Posts”
src={post.cover}Image: “Cover” → hello.jpg, from “Posts”

A click on the chip opens the data on that item, when midcode knows where it’s kept:

The list goes overOpens
A collection in your files (an array in a module, a JSON file, a folder of Markdown)The CMS, on that item
Rows read from a table (Prisma, Supabase, Drizzle, a query)Database, on that table
A Shopify store’s products, collections, pages, blog posts or menusStore, on that product

Anything else (an API, a variable midcode can’t follow) shows the code it reads, and the chip opens that line.

When the words can still be changed on the canvas

If the list is a collection in your files, the words are written there, so the page can change them: the text field stays, under the field’s name, and double-clicking the text on the canvas edits it. The change is written into the collection, the same edit the CMS would make.

src/content/posts.ts
 export const posts = [-  { title: 'Hello world', slug: 'hello-world' },+  { title: 'Hola mundo', slug: 'hello-world' },   { title: 'Second post', slug: 'second-post' }, ]

If the words are kept outside the project (a store’s product, a row of a database), there’s no field, and a double click on the text, with the item open, says where it comes from and offers to open it. Nothing is written to the page, so what the page says and what the data says can’t drift apart.

Connect something to a field

What an item shows can be connected to a field of the list’s item from the right panel, the way a collection list works in Framer: you pick the field, and midcode writes the read. This is for lists written in JSX, on a page.

Select the item, or something inside it. A plus, “Connect to a field of Products”, sits at the left of what can take a field:

  • on an element written in the list’s own code: at the left of the name of its “Text”, “Link” and “Image” sections, and of “Alt text”;

  • on an item that is a component (or a component inside the item): at the left of each of its props and of “Variant”, in the section named after the component, and of “Text” when it has words between its tags (<Button>See it</Button>).

Press the plus. A menu opens with the list’s name and the fields of its item: a collection’s fields, a table’s columns, or the fields the loop’s code already reads when midcode can’t tell where the list comes from. Each field has the icon of what it holds and, dimmed, what the first item has there. The fields that fit come first (images for an image, links for a link, texts for a text); the rest are under a line. With nine fields or more you can type to search.

Pick one. The code reads that field there, so every item shows its own, and the row turns into the field’s name with this item’s value in a violet chip.

src/app/page.tsx
 {products.map((p) => (   <li key={p.slug}>-    <h3>Arc lamp</h3>-    <img src="/img/arc.jpg" alt="A lamp" />-    <Badge label="New" />+    <h3>{p.name}</h3>+    <img src={p.image} alt={p.name} />+    <Badge label={p.finish} />   </li> ))}

Take a connection away, or change its field

Point at the violet chip of something connected to a field of the list. A ✕, “Disconnect”, shows at its end. Press it and what this item shows is written in the field’s place:

src/app/page.tsx
-    <h3>{p.name}</h3>+    <h3>Dome</h3>

Every item then says “Dome”: there is one item in the code. That’s what you want before giving the element words of its own, or before connecting it to something else.

To read another field, right-click the chip and choose “Change field…”: the same menu opens, on the field it reads now. “Disconnect” is in that menu too.

Connecting, changing a field and disconnecting are one edit each: ⌘Z takes it back, and the file shows in Publish.

Through components and includes

The item is often a component, and the value reaches the element as a prop:

src/app/page.tsx
{posts.map((post) => (
  <Card key={post.slug} title={post.title} href={`/blog/${post.slug}`} />
))}
src/components/Card.tsx
export function Card({ title, href }: { title: string; href: string }) {
  return (
    <h3>
      <a href={href}>{title}</a>
    </h3>
  )
}

Select a card and its props are connected: “Title” shows this card’s title and “Href” its address, each read from “Posts”. Open the component and the link shows the same row. midcode follows the prop to the place the component is used, and from there to the loop.

The same holds for a template that is included with parameters. In Shopify’s Dawn theme the products of the home page are a loop in a section that renders a snippet:

sections/featured-collection.liquid
{%- for product in section.settings.collection.products -%}
  <li class="grid__item">
    {% render 'card-product', card_product: product %}
  </li>
{%- endfor -%}
snippets/card-product.liquid
<a href="{{ card_product.url }}">{{ card_product.title | escape }}</a>

The <li> is the “Products” layer, the link’s text is “Title” and its address is “URL”, and “Open in Store” opens that product. Which collection the section shows, and how many products, are the section’s own settings: see Section settings.

Where it works

CodeLoops midcode reads
JSX (React, Preact, Solid).map(), also after .filter(), .slice() or .sort()
Vuev-for
Svelte{#each}
Astro.map() in the template
Angular@for, *ngFor
Liquid, Jinja, Twig, Nunjucks{% for %}
Blade@foreach, @forelse
ERB.each do |item|
EJS.forEach(), for … of
PHPforeach, and WordPress’s loop (while (have_posts()))
Go templates (Hugo), Handlebars{{ range }}, {{#each}}

A value is followed through variables, imports, props and include parameters. It’s a reading of the code, never a run of it.

Limits

  • midcode reads what the code says, not what it does at run time. A list built in a way it doesn’t know (a reduce, a recursive component) is still one layer, without a name or a source.

  • Fields are connected from the panel in JSX files only, and on a page. It was tried in React (Next.js); Preact and Solid are untried. In Vue, Svelte, Astro and templates the connections show, and are changed in the code. While a component is open, the plus makes the component’s variables instead.

  • The plus needs a loop that names its item ((post) => …). A loop that takes the item apart where it starts (({ title, slug }) => …) has no plus: a new field would have to be added there, in the code.

  • What’s written inside a component the list uses can’t read the item, so it has no plus: connect the component’s prop, where the list uses the component.

  • “Change field…” is there when the value is the field and nothing else (post.title). A value built around a field (`/blog/${post.slug}`) can be disconnected, not pointed at another field: the rest of it would be lost. Change it in the code.

  • A prop has the ✕ when its component shows the prop whole (<h3>{title}</h3>, src={image}), because only then is what the page shows what the prop was given. A prop the page shows beside other words (From {price}), or doesn’t show at all (a yes / no, a variant), has “Change field…” and no ✕. A component that adds words of its own around what’s between its tags ({children} →) is not told apart: disconnecting its text writes those words too, and ⌘Z takes it back.

  • A text longer than 500 characters has no ✕: the panel doesn’t have all of it to write back.

  • A table is recognized by how the code reads it (prisma.post.findMany(), supabase.from('posts'), a from posts in a query). The chip opens that table when the connected database has it.

  • In templates that a server fills (Django, Rails, Laravel), the list’s data comes from code midcode doesn’t follow: the chip shows the field and the list, and a source only when a collection of the project has the same name.

  • “Open in CMS” lands on the item when it can be told which one it is: by the words it shows, or by its position when the list isn’t filtered or sorted.

  • Which product a card shows is read from the links inside it (/products/gift-card).

  • Adding, removing and reordering the items is done where the data is: the collection, the table, the store.