Skip to content

Step 1 of 5 · Reading · ~4 min

Read

Reactive Foundations

What Frontend Frameworks Solve

By the end of this lesson your program turns a tree of nodes into the exact HTML a browser would receive, escaping included. That is the smallest honest slice of what a framework does: you describe the UI as data, and the framework produces the page.

The problem

A page is a tree of DOM nodes owned by the browser. Your program's state lives elsewhere, in variables. Keeping the two in sync by hand means every state change needs its own list of edits: find this node, change that text, add a class, remove a row. With one counter that is easy. With forty pieces of state feeding two hundred nodes, some edit is always forgotten, and the screen quietly stops matching the data.

A framework changes the question you answer. Instead of "what edits turn the old screen into the new one", you say "what should the screen look like for this state", and the framework works out the edits. The screen becomes a function of state, so the two cannot disagree.

Every framework bundles four things: a component model (UI pieces you compose), declarative rendering, a way to hold state, and reactivity (change the state, the screen follows). They split into two families:

  • Virtual DOM (React, Vue, Preact): render returns a lightweight tree, the framework compares it with the previous tree and applies only the differences.
  • Fine-grained reactivity (Solid, Svelte): a compiler works out which expression feeds which node, so there is no tree to compare.

HTMX is the opposite bet: the server sends ready HTML and the browser swaps it in. This course builds the first family, a tiny React, one capability per lesson. Every exercise is a program that reads a script on stdin and prints the result, so no browser is involved.

Stage 1: a tree and its HTML

Everything starts with a node tree. Your program will receive one as lines:

OPEN div id=app className=card
TEXT Hello
OPEN br
CLOSE
TEXT world
CLOSE

and must print what a browser would be sent:

<div id="app" class="card">Hello<br>world</div>

Four rules produce that:

  1. An element is its open tag, its children in order, then its end tag. Attributes appear in the order given, each as name="value". A bare attribute (disabled) is written bare. That is how HTML booleans work, and disabled="false" would still disable the control.
  2. Void elements (area base br col embed hr img input link meta source track wbr) have no children and no end tag. A written </br> is not an end tag; browsers read it as a second <br>.
  3. Two attribute names are renamed on the way out, because JSX cannot use the words class and for: className becomes class and htmlFor becomes for.
  4. Text and attribute values are escaped. The five characters below are replaced; everything else, spaces included, passes through untouched.
CharacterBecomes
&&amp;
<&lt;
>&gt;
"&quot;
'&#x27;

Why this belongs in the framework and not in your code: a text node is data. If a user's display name is <img src=x onerror=steal()>, the only safe thing to emit is &lt;img src=x onerror=steal()&gt;, which the browser shows as plain characters. React escapes by default for exactly this reason, and having to opt out (dangerouslySetInnerHTML) is the whole point of that name. The same function serves attribute values: a " inside a value would otherwise end the attribute early and let the rest be read as new attributes.

Worked trace for TEXT a < b && "ok" inside a p: a stays, the space stays, < becomes &lt;, && becomes &amp;&amp;, and each quote becomes &quot;. The result is <p>a &lt; b &amp;&amp; &quot;ok&quot;</p>.

Common mistakes

  • Escaping & last. Replace < first and you get &lt;; replacing & afterwards turns it into &amp;lt;, which displays as the literal text &lt;. & must be handled first, or all five in a single pass.
  • Writing an end tag for a void element: <br></br> or <img ...></img>.
  • Forgetting the rename. className="card" in the output is an attribute browsers ignore, so the styling silently disappears.
  • Writing a bare attribute as checked="" or checked="true". Bare means bare.
  • Trimming text. The spaces inside a text node are content. TEXT world (two spaces) is the text world.

Your exercise

The starter reads the lines and builds the node tree for you (kind 0 is an element, 1 is text; each node has its tag or text, its attributes in order, and its children). You write the two functions that turn a tree into a string: the escape and the render. The mistake most likely to cost you a hidden test is the escape order above.

Up nextVirtual DOM TreesReactive Foundations

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…