···11----
22-title: Imaginary HTML
33-date: '2025-05-01'
44-spoiler: Tags on both sides.
55----
66-77-Here's a piece of HTML:
88-99-```js
1010-<html>
1111- <body>
1212- <p>Hello, world</p>
1313- </body>
1414-</html>
1515-```
1616-1717-Imagine this was the only piece of HTML you've ever seen in your life. If you had complete freedom, which features would you add to HTML, and in what order?
1818-1919----
2020-2121-### Tags
2222-2323-Personally, I'd like to start by adding a way to define my own HTML tags.
2424-2525-It doesn't need to be complicated. We can just use JavaScript functions:
2626-2727-```js {3,7-9}
2828-<html>
2929- <body>
3030- <Greeting />
3131- </body>
3232-</html>
3333-3434-function Greeting() {
3535- return <p>Hello, world</p>
3636-}
3737-```
3838-3939-To make this work, let's say that whenever the HTML is sent over the network--that is, *serialized*--the server must replace those tags with the output they return:
4040-4141-```js {3}
4242-<html>
4343- <body>
4444- <p>Hello, world</p>
4545- </body>
4646-</html>
4747-```
4848-4949-When there are no functions left to call, the HTML is ready to be sent.
5050-5151-Neat feature, huh?
5252-5353-Good thing we got it in early.
5454-5555-It might influence how we approach everything else.
5656-5757----
5858-5959-### Attributes
6060-6161-Let's support passing attributes to tags and interpolating values into the markup.
6262-6363-```js {3-4,9}
6464-<html>
6565- <body>
6666- <Greeting name="Alice" />
6767- <Greeting name="Bob" />
6868- </body>
6969-</html>
7070-7171-function Greeting({ name }) {
7272- return <p>Hello, {name}</p>
7373-}
7474-```
7575-7676-Of course, there's no reason why those arguments have to be *strings*.
7777-7878-We might want to pass an object to `Greeting` instead:
7979-8080-```js {3-4,10-12}
8181-<html>
8282- <body>
8383- <Greeting person={{ name: 'Alice', favoriteColor: 'purple' }} />
8484- <Greeting person={{ name: 'Bob', favoriteColor: 'pink' }} />
8585- </body>
8686-</html>
8787-8888-function Greeting({ person }) {
8989- return (
9090- <p style={{ color: person.favoriteColor }}>
9191- Hello, {person.name}
9292- </p>
9393- );
9494-}
9595-```
9696-9797-Objects let us group related stuff together.
9898-9999-According to the [earlier rule](#tags), *serializing* the HTML above would produce:
100100-101101-```js
102102-<html>
103103- <body>
104104- <p style={{ color: 'purple' }}>Hello, Alice</p>
105105- <p style={{ color: 'pink' }}>Hello, Bob</p>
106106- </body>
107107-</html>
108108-```
109109-110110-Still, we haven't gotten rid of all objects.
111111-112112-What should we do with those `{ color: '...' }` objects?
113113-114114----
115115-116116-### Objects
117117-118118-The "real" HTML we know and love has no first-class notion of objects. If we wanted to output some "real" HTML, we'd have to turn `style` into a string:
119119-120120-```js {3,4}
121121-<html>
122122- <body>
123123- <p style="color: purple">Hello, Alice</p>
124124- <p style="color: pink">Hello, Bob</p>
125125- </body>
126126-</html>
127127-```
128128-129129-But if we're reimagining HTML, we don't have to abide by the same limitations. For example, we could decide that our imaginary HTML *serializes into* JSON:
130130-131131-```js {6,10}
132132-["html", {
133133- children: ["body", {
134134- children: [
135135- ["p", {
136136- children: "Hello, Alice",
137137- style: { color: "purple" }
138138- }],
139139- ["p", {
140140- children: "Hello, Bob",
141141- style: { color: "pink" }
142142- }]
143143- ]
144144- }]
145145-}]
146146-```
147147-148148-This lets us keep the `style` objects--*or any objects we might want to send*--intact.
149149-150150-This strange JSON representation isn't particularly interesting or useful yet. But going forward, we'll consider this representation as our primary output format. We can always generate "real" HTML *from* this representation, but not vice versa.
151151-152152----
153153-154154-### Async Tags
155155-156156-We're previously hardcoded some objects into our HTML:
157157-158158-```js {3-4}
159159-<html>
160160- <body>
161161- <Greeting person={{ name: 'Alice', favoriteColor: 'purple' }} />
162162- <Greeting person={{ name: 'Bob', favoriteColor: 'pink' }} />
163163- </body>
164164-</html>
165165-166166-function Greeting({ person }) {
167167- // ...
168168-}
169169-```
170170-171171-But we could grab them from somewhere else.
172172-173173-Let's read the data from the filesystem:
174174-175175-```js {3-4}
176176-<html>
177177- <body>
178178- <Greeting person={JSON.parse(await readFile('./alice123.json', 'utf8'))} />
179179- <Greeting person={JSON.parse(await readFile('./bob456.json', 'utf8'))} />
180180- </body>
181181-</html>
182182-183183-function Greeting({ person }) {
184184- // ...
185185-}
186186-```
187187-188188-Actually, this looks a bit repetitive--let's move the `readFile` *into* the `Greeting`:
189189-190190-```js {3-4,8-10}
191191-<html>
192192- <body>
193193- <Greeting username="alice123" />
194194- <Greeting username="bob456" />
195195- </body>
196196-</html>
197197-198198-async function Greeting({ username }) {
199199- const filename = `./${username.replace(/\W/g, '')}.json`;
200200- const person = JSON.parse(await readFile(filename, 'utf8'));
201201- return (
202202- <p style={{ color: person.favoriteColor }}>
203203- Hello, {person.name}
204204- </p>
205205- );
206206-}
207207-```
208208-209209-We'll have to slightly amend our specification to allow this. To *send* HTML, we'll have to *wait* for all custom tags to resolve (and to be replaced with their output).
210210-211211-The end result is still the same:
212212-213213-```js
214214-["html", {
215215- children: ["body", {
216216- children: [
217217- ["p", {
218218- children: "Hello, Alice",
219219- style: { color: "purple" }
220220- }],
221221- ["p", {
222222- children: "Hello, Bob",
223223- style: { color: "pink" }
224224- }]
225225- ]
226226- }]
227227-}]
228228-```
229229-230230-(If we wanted, we could then convert this JSON into the "real" HTML:)
231231-232232-```js
233233-<html>
234234- <body>
235235- <p style="color: purple">Hello, Alice</p>
236236- <p style="color: pink">Hello, Bob</p>
237237- </body>
238238-</html>
239239-```
240240-241241-But note how our original "imaginary HTML" allowed us to *name* this concept:
242242-243243-```js {3-4}
244244-<html>
245245- <body>
246246- <Greeting username="alice123" />
247247- <Greeting username="bob456" />
248248- </body>
249249-</html>
250250-251251-async function Greeting({ username }) {
252252- // ...
253253-}
254254-```
255255-256256-It allowed us to create a [self-contained abstraction](/impossible-components/#local-state-local-data) that loads its own data.
257257-258258-Cool beans!
259259-260260----
261261-262262-### Event Handlers