React for Two Computers
Two things, one origin.
React for Two Computers
April 9, 2025
I’ve been trying to write this post at least a dozen times. I don’t mean this figuratively; at one point, I literally had a desktop folder with a dozen abandoned drafts. They had wildly different styles—from rigoruous to chaotically cryptic and insufferably meta; they would start abruptly, chew on themselves, and eventually trail off to nowhere. One by one, I threw them all away because they all sucked.
It turns out that I wasn’t really writing a post; I was actually preparing a talk. I was pretty far into the process of writing this post when I had that realization. Oops! Thankfully, the React Conf organizers let me present a new talk on a short notice, and I did that eight months ago. You can watch React for Two Computers below.
It’s about everyone’s favorite topic, React Server Components. (Or maybe not.)
I’ve given up on the idea of converting this talk into a post form, nor do I think it’s possible. But I wanted to jot down a few notes that are complementary to the talk. I’m going to assume that you have watched the talk itself. This is just the stuff that wasn’t coherent enough to make the cut—the loose threads I couldn’t tie together.
Act 1
Recipes and Blueprints
What is the difference between a tag and a function call?
Here’s a tag:
<p>Hello</p>
Here’s a function call:
alert('Hello');
One difference is that < and > are hard and spiky and ( and ) are soft and round. But that’s not what I mean. These are just visual differences. But what is the difference in how they work, what they mean, in what we expect from them?
Of course, there is no particular meaning to a tag or a function call if you don’t specify which language you’re using. For example, a JavaScript function call might not behave the same way as a Haskell function call; an HTML tag might not behave the same way as a ColdFusion tag. Nevertheless, there are some things that we expect from a tag or a function call precisely because we’re familiar with how they work in popular languages. The spiky < and > carry a set of associations and intuitions, just like the soft ( and ) do. I want to dig into these intuitions.
Let’s start with what alert('Hello') and <p>Hello</p> have in common:
- We refer to a function or a tag by its name. By convention, a function call often starts with a verb (
createElement,printMoney,querySelectorAll), while tags are usually named with nouns (likepfor a paragraph). This isn’t a hard rule (alertis both;bstands for bold) but it’s true more often than not. (Why is that?)
- We can pass information to a function or a tag. Earlier, we were passing a piece of text (
'Hello') to the tag and the function. But we’re not limited to passing a single string. Within a JavaScript function call, we can pass multiple arguments including strings, numbers, booleans, objects, and so on. Within an HTML tag, we can pass multiple attributes, but their values cannot be objects or other rich data structures—which is quite limiting. Thankfully, tags in JSX (as well as in many HTML-based template languages) let us pass objects and any other rich values.
- Both function calls and tags can be deeply nested. For example, we could write
alert('Hello, ' + prompt('Who are you?'))to express the relationship between these two different function calls: the result of the innerpromptcall gets combined with a string, and is then passed to the outeralertcall. (Try it in your console if you’re not sure what this does.) Although nesting is fairly common with function calls, with tags nesting really is the name of the game. You hardly ever see a tag completely alone and not surrounded by other tags. (Why is that?)
Clearly, function calls and tags are very similar. They let us pass some information to a named thing, and if needed, they let us elaborate by nesting further (passing some more information to some named thing (nesting as much as we need (yay!)))
We’re also starting to get a few hints of the fundamental differences between them. For one, function calls tend to be verbs while tags tend to be nouns. Also, you’ll encounter deeply nested tags more often than deeply nested function calls.
What’s up with that?
Let’s start with the latter. Why do tags tend to cling to each other? Are tags just naturally predisposed to gravitate towards other tags until they—yoink!—click together? Perhaps those spiky bois really do yearn for connection? That may be so, but consider this: maybe we like to use tags for deeply nested structures because we can see the </end> of every tag and don’t have to guess which ) is closing.
Tags don’t lead to deep nesting—rather, we choose to use tags for deep nesting. (Recall how broadly the JavaScript community eventually adopted JSX despite it being almost universally panned for a year or two. Nesting tags is hard to give up!)
Okay, let’s say we prefer to use tags for nesting. But why do tags tend to be nouns rather than verbs? Is this a coincidence, or is there a deeper reason for this as well?
This is more of a conjecture but I think it’s because nouns are easier to decompose than verbs. Nouns describe things, and things can often be adequately described purely as a composition of other things. For example, a building consists of floors, floors consist of rooms, rooms consist of people, and people consist of water. Note that this description is timeless—not in the sense that it’s a classic, but in the sense that it describes a snapshot in time, like a frame in the movie, or like a blueprint. You can omit time from the equation, and it’ll still be a pretty useful description.
Verbs, on the other hand, tend to describe processes which happen over time—they’re timeful! Consider a cooking recipe: “Heat the frying pan, put the butter on it, wait for the butter to melt, now pour the eggs on it.” Although there are still opportunities for composition (how does one crack an egg?), the sequencing here is crucial! You need to be constantly aware of what step happens first, what step happens next, and what kind of decisions you have to make between the steps. Unlike a blueprint, a recipe has an ordering to it, and a certain urgency to it too.
So how does this relate to tags and function calls?
A recipe prescribes a sequence of steps to be performed in an order. It’s composed of verbs but there is rarely a lot of nesting. (In fact, nesting may just obscure the ordering.) Each step may change something or depend on the previous steps, so it is important to execute the recipe in the exact order it was written, top to bottom. These recipes, also known as imperative programs, are written with function calls:
const eggs = crackEggs();
heat(fryingPan);
put(fryingPan, butter);
await delay(30000);
put(fryingPan, eggs);
A blueprint, on the other hand, describes what nouns a thing is made of. It doesn’t prescribe a specific order of operations—it only describes how the whole is broken into its parts. This is why these blueprints, also known as declarative programs, naturally end up deeply nested, and thus are more convenient to write with tags:
<Building>
<Roof />
<Floor>
<Room />
<Room>
<Person name="Alice" />
<Person name="Bob" />
</Room>
</Floor>
<Basement />
</Building>
Many real-world programs combine both techniques. For example, a typical React component combines some imperative recipes (like sequences of function calls in the event handlers) with some declarative blueprints (like the returned JSX tags).
[...]