top of page

Components Are Dead; And Now for Something Completely Different

3 hours ago
4 min read
From a Component System to a Region System - Yoav Abrahami

Introduction


The component abstraction was designed to solve a human cognitive limit: a developer cannot hold an entire UI hierarchy in their head, so we learned to encapsulate UI once and reference it across files.


But as AI agents increasingly write and maintain frontend code, that trade-off inverted. Agents struggle with multi-file indirection and slot composition seams, but thrive on local context they can see and verify in a single file. Components are the abstraction for humans; regions are the abstraction for agents.


Component systems attempt to bridge this gap by paying repeatedly at runtime - reconstructing UI relationships across component boundaries (slots, context, closures, ref forwarding) on every render.


Jay,  our new frontend framework, moves this complexity to coding time. The design-system template is flattened into page-local HTML ("regions") while logic remains in a shared, headless component. The result is an architecture that delivers on three technical fronts at once: a faster runtime, a simpler framework, and a verifiable workspace for agents.


  • The Appearance: Flattened into the local page as editable, verifiable markup.

  • The Logic: Encapsulated in shared, headless components (bring-your-own-markup).

  • The Guardrails: Validated by a deterministic CLI loop (validate / sync) that detects drift at build time.


1. The Shift


A component packages appearance, structure, and data behind one boundary. Any variation the author didn't anticipate needs a new API point - a prop, a variant, a slot.


A region splits that responsibility:


  • Appearance & structure → flattened into the page as local, editable HTML.

  • Data & logic → kept as props to a headless component.


COMPONENT MODEL (runtime resolution)
  Page → <Card props> → <Slot> → render props → context / ref seams

REGION SYSTEM (coding-time flattening)
  Page (flattened HTML) ← headless logic (direct signal bindings)


2. The Proof: Resolving Variation


Component way - to customize a card's footer, you project a subtree through a slot:


// card.tsx - author leaves a hole
function Card({ title, footerSlot }: CardProps) {
    return (
        <div className="card">
            <h3>{title}</h3>
            <div className="card__footer">{footerSlot}</div>
        </div>
    );
}

// page.tsx — consumer projects a subtree across the boundary
<Card footerSlot="{<span" title="Product">Save <strong>50%</strong> today</span>} />

The footerSlot is authored in page scope but rendered in component scope. The runtime tracks reactive context, prevents leaks, and scopes refs across that seam on every render.


Region way - the template is flattened into the page; the consumer edits local HTML. No slots, no render props, no projected subtree:


<!-- page.jay-html — markup is local; logic stays linked via script -->
<script
  type="application/jay-headless"
  contract="card"
  src="./node_modules/@org/ui/card/card.ts"
  template="./node_modules/@org/ui/card/card.jay-html"
></script>

<jay:card ref="heroCard" productId="sku-100">
  <div class="card">
    <h3>Product</h3>
    <div class="card__footer">
      <span>Save <strong>50%</strong> today</span>
    </div>
  </div>
</jay:card>

The page owns the markup. The <jay:card> tag passes runtime props (productId) straight to the imported headless logic.



3. Still Fully Reactive


Flattening is not freezing:

  • No virtual DOM. Each dynamic binding compiles to a tiny update closure holding a direct coordinate reference to one DOM node (adoptText, adoptBase).

  • Phase-gated hydration. Only fast + interactive values run client-side adoption; slow/static values resolve at SSR and ship as inert HTML.

  • Targeted updates. When productId or an internal signal changes, only the closures bound to the affected nodes fire. The subtree is never re-rendered.



4. Copies Stay Honest


Flattening makes copies; two deterministic operations keep them from drifting:


<!-- CONSUMER (page.jay-html): "kept my edit; preserve during sync" -->
<p class="card__body" override="children">Custom local copy</p>

<!-- AUTHOR (card.jay-html): "this node is for consumer content; never flag" -->
<p class="card__body" jay-content>Default template copy</p>

  • validate - AST-diffs flattened regions against source templates; warns on drift that isn't marked override or jay-content (e.g. REGION-NOT-LINKED, REGION-OVERRIDE-NON-CONTENT).

  • sync - re-flattens from updated upstream templates, carrying marked local edits forward. Structural breaks (a deleted upstream node) revert the sequence to source with a warning rather than producing a silent hybrid.



5. Trade-Offs


From a Component System to a Region System


6. Components Are for Humans. Regions Are for Agents.


Stated up front, proven here. Regions optimize for exactly the constraints an agent has:


  • Zero-hop context. The full rendering truth of a page is in one file - nothing to reconstruct from files that each tell half the story.

  • Verifiable by construction. Instead of guessing whether an edit broke the design system, the agent runs validate and self-corrects against machine-readable diagnostics.

  • Locality without drift. Agents edit local markup freely; sync reconciles against upstream while preserving approved overrides.


The pattern underneath all three is locality of concerns - the full truth of a thing kept in one place, not scattered behind an API. It's a strong principle for humans; for agents it's a decisive one. A human can tolerate indirection and reassemble the component tree in their head; an agent pays for every hop it can't see and verify in one place. That's the bet behind "components are dead": locality of concerns was the right default for the human era of software, and it only gets stronger as agents take the keyboard.


Summary Thesis


Component Systems force frameworks to act as complex runtime accounting engines - spending execution cycles on instance tracking, context providers, and slot projection across multiple files.


Region Systems resolve composition once at coding time through template flattening and AST validation. They give the browser plain HTML wired to fine-grained signals, give the framework a radically smaller runtime surface, and give AI agents a zero-indirection workspace with closed-loop verification.


Full deep-dive (DL#181-DL#196), slot trade-offs, compiler details: From a Component System to a Region System (Full Article).



Yoav Abrahami

This post was written by Yoav Abrahami

More of Wix Engineering's updates and insights: 


Comments


The Latest Posts:

  • _FrontendDevelopment
  • _FrontendDevelopment

Components Are Dead; And Now for Something Completely Different

Kickstart your engineering career at Wix

Kickstart your engineering career at Wix

Our AI Agent’s Design Looked Complete. 3 Runtime Checks Proved It Wrong

  • _AIEngineering
  • _AIEngineering

No Dashboard, No Menus: How We Built an Interface Around Intent

bottom of page