02

The Section Contract

Case study 1: SpatialX Orca. A page is `{ page, sections: [...] }`, and every section on every screen is the same tiny shape — a DynamicSection. • Day 2 zooms into that shape: an `id`, an `options` bag, and an `actions` map. The id is looked up in a mapper to find the component; an unknown id falls back safely instead of crashing.

A page is `{ page, sections[] }`

Day 1 said the UI is data. SpatialX makes that concrete with one envelope: a page is an object with a name and an ordered list of sections. A section is one self-contained region of the screen. The client owns no screens of its own — it reads this list and draws each section in order.

The smallest possible page is a name and an empty list. It renders to a blank projects page, and that emptiness is the point: nothing bespoke exists yet.

{ "page": "projects", "sections": [] }

Grow the list and the screen grows with it. Here the server has added three sections; the client draws a header, a grid of slides, and an upload control — in that order — without shipping a line of screen code.

{ "page": "projects", "sections": [
  { "type": "header",     "title": "Projects" },
  { "type": "image_list", "title": "My projects", "data": "project_slides" },
  { "type": "upload",     "title": "Upload slide", "accept": "wsi" }
] }

Because the layout is the list, the server can rearrange, add, or remove regions of a page just by editing this array.

The DynamicSection shape

The short entries above are the abbreviated form. In full, every section is the same small object with three parts, and SpatialX calls that object a DynamicSection: an id that names its kind, an options bag of layout hints, and an actions map of things it can do. The examples in the previous section wrote the identifying name as type; the full contract keys it as id, and that id is the lookup key.

{
  "id": "images_section",
  "options": { "selectable": true },
  "actions": {
    "load":          { "url": "/folders/42/sections/images", "method": "GET" },
    "read_multiple": { "url": "/folders/42/images",          "method": "GET" },
    "create_one":    { "url": "/folders/42/images",          "method": "POST" },
    "update_one":    { "url": "/images/<int:id>",            "method": "PATCH" },
    "delete_one":    { "url": "/images/<int:id>",            "method": "DELETE" }
  }
}

Read the three parts:

  • id — the section's kind and its registry key. "images_section" is what the client looks up to decide which component draws this region.
  • options — a small bag of layout hints the component reads. Here selectable: true says the grid allows selection.
  • actions — a named map of things this section can do, each a { url, method } link. load fetches the section; read_multiple / create_one / update_one / delete_one drive its data. Day 3 is entirely about how the client follows these links, so for now just note that behavior travels in the data too.

Every DynamicSection on every SpatialX screen has exactly these three parts. Learn one section, and you can read them all.

id → mapper → lazy component

How does "id": "images_section" become an actual grid on screen? Through a mapper — SpatialX's registry — a lookup table from each section id to a React component. The renderer walks the sections in order, looks each id up in the mapper, mounts the matching component, and hands it that section's options and actions.

The component is lazy, meaning its code is only downloaded the first time a section of that kind actually appears on a page. A screen that never shows an AI-models table never pays to load the AI-models component. So the mapper does double duty: it routes each id to the right component and it keeps the initial download small by loading each kind on demand.

Options carry the layout

The same section kind can look different on different pages, and the difference rides in options — never in new client code. The component reads its options bag and lays itself out accordingly. A few real ones from SpatialX:

{ "layout": "table", "pageSize": 3, "sortable": true }
{ "layout": "deck", "cardLabels": true }
{ "layout": "table", "showBars": true }

One section can render as a paginated, sortable table on one page and as a card deck on another, because the server sent different options. This is the boundary from Day 1 holding: the server decides the layout by choosing options; the client only knows how to honor table, deck, pageSize, and the rest. Options are configuration, so changing a layout is a data edit, not a code change.

Every section is the same shape

Here is the payoff of a single contract: wildly different regions of the product are all the same { id, options, actions } object, just filled in differently. The table below lines up seven real SpatialX sections. Read down the columns — the shape never changes; only the fill does.

Section idoptions (layout)actions it carries
images_sectionselectable grid / deckload + full CRUD (read/create/update/delete)
folders_sectiontableload + read_multiple + create/update/delete
ai_projects_sectiondeck, cardLabelsload + read_multiple + create/update/delete
canvas_viewer_sectioncanvasload, dzi_read, canvas_save
annotations_manager_sectionlistload + read_multiple + create/update/delete
ai_models_sectiontable, pageSize 3, sortableload, model_read_multiple, metrics + create/update
classes_statistics_sectiontable, showBarsload + calculate_statistics (read-only)

Notice the last row. classes_statistics_section is read-only, so its actions map simply omits create, update, and delete — the same contract shrinks to fit a section that only reads. Nothing special is needed for the read-only case; it is just fewer keys in the same shape. (Day 4 builds these sections out in full and shows the CRUD-to-read-only flex up close.)

Adding a section

Because every section is the same shape, adding a genuinely new kind of region is a small, mechanical change in exactly three places — and the client work is at most one component.

The division of labor is the Day 1 boundary again. You register how the new section looks (its component) exactly once; from then on the server decides when it appears and where — by placing its id in a page's sections list. No page is edited on the client; a page grows because the server's list grew.

Unknown type → safe fallback

One question decides whether an SDUI system is safe: what happens when the server sends a section id the client does not recognize? A newer server might introduce a section an older client has never heard of. The rule SpatialX asserts is that an unknown id must fall back safely — render nothing (or a neutral placeholder) for that one region — and never crash the page around it.

This is the whole reason the section contract is worth its cost: because the client degrades one unknown section instead of failing the screen, the server is free to ship new section kinds without waiting for every client to catch up. This is a first look at forward compatibility — a client tolerating data newer than itself — and Day 8 is devoted to it across both case studies, where the trade-offs get sharper (SpatialX leans on this client-side fallback; Alphazed, serving many app versions at once, pushes the same guarantee onto the server).

Key takeaways

  • A SpatialX page is { page, sections: [...] } — an ordered list of sections; the client owns no screens and just draws the list.
  • Every section is the same DynamicSection shape: an id (its kind and lookup key), an options bag (layout hints), and an actions map ({ url, method } links).
  • The id is looked up in a mapper (the registry) that returns a lazy React component — loaded on demand, so unused section kinds cost nothing up front.
  • options carry layout (table / deck / pageSize / sortable / showBars), so the same section kind can look different on different pages with no code change.
  • Because the shape is fixed, seven very different regions are all { id, options, actions }; a read-only section simply omits the write actions — the contract shrinks to fit.
  • Adding a section is one id + one mapper entry + one component; the server then decides when and where it appears.
  • An unknown section id falls back safely (skip the region, never crash), which is what lets the server ship new section kinds ahead of older clients — the first taste of forward compatibility.

Checklist

  • [ ] I can write the page envelope { page, sections: [...] } and grow it from empty.
  • [ ] I can name the three parts of a DynamicSection and say what each does.
  • [ ] I can trace how a section id becomes a component through the mapper, and explain what "lazy" buys.
  • [ ] I can show how options change a section's layout without changing its code.
  • [ ] I can explain why seven different sections are all the same shape, and how a read-only section fits.
  • [ ] I can list the three places a new section touches and say who decides when it appears.
  • [ ] I can explain what happens on an unknown section id and why safe fallback enables forward compatibility.