And Now You Understand React Server Components

(wish me luck)

Let's wake up

Your brain needs this 🧠

Let's build a framework with
React Server Components!

build underscore.js from scratch

The Rules

  • No Bundler
  • No TypeScript
  • No JSX Transform
  • No optimizations
  • No deps*

*Only official react, react-error-boundary, and hono.js

import { createElement as h } from 'react'

What we're building

  • API:Β to fetch and render Server Components
  • Compiler:Β to transform Client Components*
  • Router:Β to route on the client and fetch updated Server Components

*It's not really a compiler, but a node loader (runtime module transformer)

Taken for granted

  • You're already optimistic about RSCs
  • You know RSC basics ("use client" etc.)
  • You're smart enough to not do this in prod
  • You're willing to dive in for details later

Coming to EpicReact.devΒ soon

The App

SPA

No SSG

No client router yet

The Project

// ui/app.js

import { Suspense, createElement as h, use } from 'react'
import { createRoot } from 'react-dom/client'
import { App } from './app.js'

const initialLocation = location.pathname + location.search
const initialDataPromise = fetch(`/api${initialLocation}`)
	.then(r => r.json())

function Root() {
	const { shipId, search, ship, shipResults } = use(initialDataPromise)
	return h(App, { shipId, search, ship, shipResults })
}

// ...
// server/app.js

// ...

app.use('/*', serveStatic({ root: './public', index: '' }))
app.use(
	'/ui/*',
	serveStatic({
		root: './ui',
		onNotFound: (path, context) => context.text('File not found', 404),
		rewriteRequestPath: path => path.replace('/ui', ''),
	}),
)

// ...

app.get('/api/:shipId?', async context => {
	const shipId = context.req.param('shipId') || null
	const search = context.req.query('search') || ''
	const ship = shipId ? await getShip({ shipId }) : null
	const shipResults = await searchShips({ search })
	const data = { shipId, search, ship, shipResults }
	return context.json(data)
})

app.get('/:shipId?', async context => {
	const html = await readFile('./public/index.html', 'utf8')
	return context.html(html, 200)
})

// ...

API ➑️ RSC

{
    "shipId": "d3b8aa65ffe6c",
    "search": "h",
    "ship": {
        "id": "d3b8aa65ffe6c",
        "name": "Planet Hopper",
        "image": "/ships/d3b8aa65ffe6c.webp",
        "topSpeed": 4,
        "hyperdrive": false,
        "weapons": [
            {
                "name": "Laser",
                "type": "beam",
                "damage": 10
            }
        ]
    },
    "shipResults": {
        "ships": [
            {
                "name": "Star Hopper",
                "id": "3ba8aa65ffe6c"
            },
            {
                "name": "Planet Hopper",
                "id": "d3b8aa65ffe6c"
            },
            {
                "name": "Stealth Cruiser",
                "id": "6c86fca8b9086"
            },
            {
                "name": "Battleship",
                "id": "fdc13cb488bf1"
            },
            {
                "name": "Dreadnought",
                "id": "d486d48b82b81"
            },
            {
                "name": "Scout Ship",
                "id": "ec7a3f950f99f"
            },
            {
                "name": "Transport Ship",
                "id": "670003aed3795"
            },
            {
                "name": "Gunship",
                "id": "b442531ea32b2"
            },
            {
                "name": "Mining Ship",
                "id": "627c497212456"
            },
            {
                "name": "Research Vessel",
                "id": "441f7092a8d44"
            },
            {
                "name": "Medical Ship",
                "id": "0268fc4817ad1"
            },
            {
                "name": "Cargo Ship",
                "id": "1ae7b4b92036b"
            }
        ]
    }
}

/api/d3b8aa65ffe6c?search=h

1:{"name":"App","env":"Server","owner":null}
0:D"$1"
3:{"name":"SearchResults","env":"Server","owner":"$1"}
2:D"$3"
2:[["$","li","Star Hopper",{"children":["$","a",null,{"href":"/3ba8aa65ffe6c?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/3ba8aa65ffe6c.webp?size=20","alt":"Star Hopper"},"$3"],"Star Hopper"]},"$3"]},"$3"],["$","li","Planet Hopper",{"children":["$","a",null,{"href":"/d3b8aa65ffe6c?search=h","style":{"fontWeight":"bold"},"children":[["$","img",null,{"src":"/img/ships/d3b8aa65ffe6c.webp?size=20","alt":"Planet Hopper"},"$3"],"Planet Hopper"]},"$3"]},"$3"],["$","li","Stealth Cruiser",{"children":["$","a",null,{"href":"/6c86fca8b9086?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/6c86fca8b9086.webp?size=20","alt":"Stealth Cruiser"},"$3"],"Stealth Cruiser"]},"$3"]},"$3"],["$","li","Battleship",{"children":["$","a",null,{"href":"/fdc13cb488bf1?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/fdc13cb488bf1.webp?size=20","alt":"Battleship"},"$3"],"Battleship"]},"$3"]},"$3"],["$","li","Dreadnought",{"children":["$","a",null,{"href":"/d486d48b82b81?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/d486d48b82b81.webp?size=20","alt":"Dreadnought"},"$3"],"Dreadnought"]},"$3"]},"$3"],["$","li","Scout Ship",{"children":["$","a",null,{"href":"/ec7a3f950f99f?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/ec7a3f950f99f.webp?size=20","alt":"Scout Ship"},"$3"],"Scout Ship"]},"$3"]},"$3"],["$","li","Transport Ship",{"children":["$","a",null,{"href":"/670003aed3795?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/670003aed3795.webp?size=20","alt":"Transport Ship"},"$3"],"Transport Ship"]},"$3"]},"$3"],["$","li","Gunship",{"children":["$","a",null,{"href":"/b442531ea32b2?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/b442531ea32b2.webp?size=20","alt":"Gunship"},"$3"],"Gunship"]},"$3"]},"$3"],["$","li","Mining Ship",{"children":["$","a",null,{"href":"/627c497212456?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/627c497212456.webp?size=20","alt":"Mining Ship"},"$3"],"Mining Ship"]},"$3"]},"$3"],["$","li","Research Vessel",{"children":["$","a",null,{"href":"/441f7092a8d44?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/441f7092a8d44.webp?size=20","alt":"Research Vessel"},"$3"],"Research Vessel"]},"$3"]},"$3"],["$","li","Medical Ship",{"children":["$","a",null,{"href":"/0268fc4817ad1?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/0268fc4817ad1.webp?size=20","alt":"Medical Ship"},"$3"],"Medical Ship"]},"$3"]},"$3"],["$","li","Cargo Ship",{"children":["$","a",null,{"href":"/1ae7b4b92036b?search=h","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/1ae7b4b92036b.webp?size=20","alt":"Cargo Ship"},"$3"],"Cargo Ship"]},"$3"]},"$3"]]
5:{"name":"ShipDetails","env":"Server","owner":"$1"}
4:D"$5"
4:["$","div",null,{"className":"ship-info","children":[["$","div",null,{"className":"ship-info__img-wrapper","children":["$","img",null,{"src":"/img/ships/d3b8aa65ffe6c.webp?size=200","alt":"Planet Hopper"},"$5"]},"$5"],["$","section",null,{"children":["$","h2",null,{"children":"Planet Hopper"},"$5"]},"$5"],["$","div",null,{"children":["Top Speed: ",4," ",["$","small",null,{"children":"lyh"},"$5"]]},"$5"],["$","section",null,{"children":["$","ul",null,{"children":[["$","li","Laser",{"children":[["$","label",null,{"children":"Laser"},"$5"],":"," ",["$","span",null,{"children":[10," ",["$","small",null,{"children":["(","beam",")"]},"$5"]]},"$5"]]},"$5"]]},"$5"]},"$5"]]},"$5"]
0:["$","div",null,{"className":"app","children":[["$","div",null,{"className":"search","children":[["$","form",null,{"children":["$","input",null,{"name":"search","placeholder":"Filter ships...","type":"search","defaultValue":"h","autoFocus":true},"$1"]},"$1"],["$","ul",null,{"children":"$2"},"$1"]]},"$1"],["$","div",null,{"className":"details","children":"$4"},"$1"]]},"$1"]

/rsc/d3b8aa65ffe6c?search=h

<App />
 ➑️ renderToPipeableStream
 ➑️ 1:{"name":"App"...
 ➑️ {type:"div",props:{....
 ➑️ createFromFetch
 ➑️ root.render
 ➑️ <div>...

Async Components πŸ”€

Streaming 🌊

Server Context 🧞

You'll figure it out...

"use client" πŸͺ©

import { createElement as h } from 'react'
import { getShip } from '../db/ship-api.js'
import { shipDataStorage } from '../server/async-storage.js'
import { EditableText } from './edit-text.js'
import { getImageUrlForShip } from './img-utils.js'

export async function ShipDetails() {
	const { shipId } = shipDataStorage.getStore()
	const ship = await getShip({ shipId })
	const shipImgSrc = getImageUrlForShip(ship.id, { size: 200 })

	return (
		<div className="ship-info">
			<div className="ship-info__img-wrapper">
				<img src={shipImgSrc} alt={ship.name} />
			</div>

			<section>
				<h2>
					<EditableText
						key={shipId}
						shipId={shipId}
						initialValue={ship.name}
					/>
				</h2>
			</section>

			{/* ... */}

		</div>
	)
}
'use client'

import { useRef, useState } from 'react'
import { flushSync } from 'react-dom'

// ...

export function EditableText({ id, shipId, initialValue = '' }) {
	const [edit, setEdit] = useState(false)
	const [value, setValue] = useState(initialValue)
	const inputRef = useRef(null)
	const buttonRef = useRef(null)
	return (
		<div>
			{edit ? (
				<form
					action={() => {
						setValue(inputRef.current?.value ?? '')
						flushSync(() => {
							setEdit(false)
						})
						buttonRef.current?.focus()
						// TODO: persist the value
					}}
				>
					{/* ... */}
				</form>
			) : (
				<button
					onClick={() => {
						flushSync(() => {
							setEdit(true)
						})
						inputRef.current?.select()
					}}
				>
					{/* ... */}
				</button>
			)}
		</div>
	)
}
2:"$Sreact.suspense"
1:{"name":"App","env":"Server","owner":null}
0:D"$1"
4:{"name":"SearchResultsFallback","env":"Server","owner":"$1"}
3:D"$4"
3:[["$","li","0",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","1",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","2",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","3",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","4",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","5",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","6",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","7",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","8",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","9",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","10",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","11",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"]]
6:{"name":"SearchResults","env":"Server","owner":"$1"}
5:D"$6"
8:{"name":"ShipFallback","env":"Server","owner":"$1"}
7:D"$8"
7:["$","div",null,{"className":"ship-info","children":[["$","div",null,{"className":"ship-info__img-wrapper","children":["$","img",null,{"src":"/img/ships/ec7a3f950f99f.webp?size=200","alt":"ec7a3f950f99f"},"$8"]},"$8"],["$","section",null,{"children":["$","h2",null,{"children":"Loading..."},"$8"]},"$8"],["$","div",null,{"children":["Top Speed: XX"," ",["$","small",null,{"children":"lyh"},"$8"]]},"$8"],["$","section",null,{"children":["$","ul",null,{"children":[["$","li","0",{"children":[["$","label",null,{"children":"loading"},"$8"],":"," ",["$","span",null,{"children":["XX ",["$","small",null,{"children":"(loading)"},"$8"]]},"$8"]]},"$8"],["$","li","1",{"children":[["$","label",null,{"children":"loading"},"$8"],":"," ",["$","span",null,{"children":["XX ",["$","small",null,{"children":"(loading)"},"$8"]]},"$8"]]},"$8"],["$","li","2",{"children":[["$","label",null,{"children":"loading"},"$8"],":"," ",["$","span",null,{"children":["XX ",["$","small",null,{"children":"(loading)"},"$8"]]},"$8"]]},"$8"]]},"$8"]},"$8"]]},"$8"]
a:{"name":"ShipDetails","env":"Server","owner":"$1"}
9:D"$a"
0:["$","div",null,{"className":"app","children":[["$","div",null,{"className":"search","children":[["$","form",null,{"children":["$","input",null,{"name":"search","placeholder":"Filter ships...","type":"search","defaultValue":"","autoFocus":true},"$1"]},"$1"],["$","ul",null,{"children":["$","$2",null,{"fallback":"$3","children":"$L5"},"$1"]},"$1"]]},"$1"],["$","div",null,{"className":"details","children":["$","$2",null,{"fallback":"$7","children":"$L9"},"$1"]},"$1"]]},"$1"]
b:I["/edit-text.js","EditableText"]
9:["$","div",null,{"className":"ship-info","children":[["$","div",null,{"className":"ship-info__img-wrapper","children":["$","img",null,{"src":"/img/ships/ec7a3f950f99f.webp?size=200","alt":"Scout Ship"},null]},null],["$","section",null,{"children":["$","h2",null,{"children":["$","$Lb","ec7a3f950f99f",{"shipId":"ec7a3f950f99f","initialValue":"Scout Ship"},null]},null]},null],["$","div",null,{"children":["Top Speed: ",11," ",["$","small",null,{"children":"lyh"},null]]},null],["$","section",null,{"children":["$","p",null,{"children":"NOTE: This ship is not equipped with any weapons."},null]},null]]},null]
5:[["$","li","Infinity Drifter",{"children":["$","a",null,{"href":"/bc4cbadf89bd3","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/bc4cbadf89bd3.webp?size=20","alt":"Infinity Drifter"},null],"Infinity Drifter"]},null]},null],["$","li","Star Hopper",{"children":["$","a",null,{"href":"/3ba8aa65ffe6c","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/3ba8aa65ffe6c.webp?size=20","alt":"Star Hopper"},null],"Star Hopper"]},null]},null],["$","li","Galaxy Cruiser",{"children":["$","a",null,{"href":"/ab267a5984523","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/ab267a5984523.webp?size=20","alt":"Galaxy Cruiser"},null],"Galaxy Cruiser"]},null]},null],["$","li","Planet Hopper",{"children":["$","a",null,{"href":"/d3b8aa65ffe6c","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/d3b8aa65ffe6c.webp?size=20","alt":"Planet Hopper"},null],"Planet Hopper"]},null]},null],["$","li","Space Taxi",{"children":["$","a",null,{"href":"/1ff1991efe029","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/1ff1991efe029.webp?size=20","alt":"Space Taxi"},null],"Space Taxi"]},null]},null],["$","li","Star Destroyer",{"children":["$","a",null,{"href":"/f3d9a88e1c234","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/f3d9a88e1c234.webp?size=20","alt":"Star Destroyer"},null],"Star Destroyer"]},null]},null],["$","li","Interceptor",{"children":["$","a",null,{"href":"/cb03cc4e5717e","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/cb03cc4e5717e.webp?size=20","alt":"Interceptor"},null],"Interceptor"]},null]},null],["$","li","Stealth Cruiser",{"children":["$","a",null,{"href":"/6c86fca8b9086","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/6c86fca8b9086.webp?size=20","alt":"Stealth Cruiser"},null],"Stealth Cruiser"]},null]},null],["$","li","Battleship",{"children":["$","a",null,{"href":"/fdc13cb488bf1","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/fdc13cb488bf1.webp?size=20","alt":"Battleship"},null],"Battleship"]},null]},null],["$","li","Dreadnought",{"children":["$","a",null,{"href":"/d486d48b82b81","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/d486d48b82b81.webp?size=20","alt":"Dreadnought"},null],"Dreadnought"]},null]},null],["$","li","Cruiser",{"children":["$","a",null,{"href":"/cfd10fcd2de6c","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/cfd10fcd2de6c.webp?size=20","alt":"Cruiser"},null],"Cruiser"]},null]},null],["$","li","Frigate",{"children":["$","a",null,{"href":"/e92cefe4f6727","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/e92cefe4f6727.webp?size=20","alt":"Frigate"},null],"Frigate"]},null]},null],["$","li","Scout Ship",{"children":["$","a",null,{"href":"/ec7a3f950f99f","style":{"fontWeight":"bold"},"children":[["$","img",null,{"src":"/img/ships/ec7a3f950f99f.webp?size=20","alt":"Scout Ship"},null],"Scout Ship"]},null]},null]]
// ui/edit-text.js
'use client'
import { createElement as h, useRef, useState } from 'react'
import { flushSync } from 'react-dom'

// ...

export function EditableText({ id, shipId, initialValue = '' }) {
	// ...
}

// πŸ‘‡ πŸ‘‡ πŸ‘‡ πŸ‘‡ πŸ‘‡ πŸ‘‡

import {registerClientReference} from "react-server-dom-esm/server";
export const EditableText = registerClientReference(function() {throw new Error("Attempted to call EditableText() from the server but EditableText is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.");},"file:///Users/kentcdodds/code/epicweb-dev/react-server-components/playground/ui/edit-text.js","EditableText");

// EditableText is the function with these properties:
{
  "$$typeof": "Symbol(react.client.reference)",
  "$$id": "file:///Users/kentcdodds/code/epicweb-dev/react-server-components/exercises/03.client-components/01.solution.loader/ui/edit-text.js#EditableText"
}
2:"$Sreact.suspense"
1:{"name":"App","env":"Server","owner":null}
0:D"$1"
4:{"name":"SearchResultsFallback","env":"Server","owner":"$1"}
3:D"$4"
3:[["$","li","0",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","1",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","2",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","3",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","4",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","5",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","6",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","7",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","8",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","9",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","10",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"],["$","li","11",{"children":["$","a",null,{"href":"#","children":[["$","img",null,{"src":"/img/fallback-ship.png","alt":"loading"},"$4"],"... loading"]},"$4"]},"$4"]]
6:{"name":"SearchResults","env":"Server","owner":"$1"}
5:D"$6"
8:{"name":"ShipFallback","env":"Server","owner":"$1"}
7:D"$8"
7:["$","div",null,{"className":"ship-info","children":[["$","div",null,{"className":"ship-info__img-wrapper","children":["$","img",null,{"src":"/img/ships/ec7a3f950f99f.webp?size=200","alt":"ec7a3f950f99f"},"$8"]},"$8"],["$","section",null,{"children":["$","h2",null,{"children":"Loading..."},"$8"]},"$8"],["$","div",null,{"children":["Top Speed: XX"," ",["$","small",null,{"children":"lyh"},"$8"]]},"$8"],["$","section",null,{"children":["$","ul",null,{"children":[["$","li","0",{"children":[["$","label",null,{"children":"loading"},"$8"],":"," ",["$","span",null,{"children":["XX ",["$","small",null,{"children":"(loading)"},"$8"]]},"$8"]]},"$8"],["$","li","1",{"children":[["$","label",null,{"children":"loading"},"$8"],":"," ",["$","span",null,{"children":["XX ",["$","small",null,{"children":"(loading)"},"$8"]]},"$8"]]},"$8"],["$","li","2",{"children":[["$","label",null,{"children":"loading"},"$8"],":"," ",["$","span",null,{"children":["XX ",["$","small",null,{"children":"(loading)"},"$8"]]},"$8"]]},"$8"]]},"$8"]},"$8"]]},"$8"]
a:{"name":"ShipDetails","env":"Server","owner":"$1"}
9:D"$a"
0:["$","div",null,{"className":"app","children":[["$","div",null,{"className":"search","children":[["$","form",null,{"children":["$","input",null,{"name":"search","placeholder":"Filter ships...","type":"search","defaultValue":"","autoFocus":true},"$1"]},"$1"],["$","ul",null,{"children":["$","$2",null,{"fallback":"$3","children":"$L5"},"$1"]},"$1"]]},"$1"],["$","div",null,{"className":"details","children":["$","$2",null,{"fallback":"$7","children":"$L9"},"$1"]},"$1"]]},"$1"]
b:I["/edit-text.js","EditableText"]
9:["$","div",null,{"className":"ship-info","children":[["$","div",null,{"className":"ship-info__img-wrapper","children":["$","img",null,{"src":"/img/ships/ec7a3f950f99f.webp?size=200","alt":"Scout Ship"},null]},null],["$","section",null,{"children":["$","h2",null,{"children":["$","$Lb","ec7a3f950f99f",{"shipId":"ec7a3f950f99f","initialValue":"Scout Ship"},null]},null]},null],["$","div",null,{"children":["Top Speed: ",11," ",["$","small",null,{"children":"lyh"},null]]},null],["$","section",null,{"children":["$","p",null,{"children":"NOTE: This ship is not equipped with any weapons."},null]},null]]},null]
5:[["$","li","Infinity Drifter",{"children":["$","a",null,{"href":"/bc4cbadf89bd3","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/bc4cbadf89bd3.webp?size=20","alt":"Infinity Drifter"},null],"Infinity Drifter"]},null]},null],["$","li","Star Hopper",{"children":["$","a",null,{"href":"/3ba8aa65ffe6c","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/3ba8aa65ffe6c.webp?size=20","alt":"Star Hopper"},null],"Star Hopper"]},null]},null],["$","li","Galaxy Cruiser",{"children":["$","a",null,{"href":"/ab267a5984523","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/ab267a5984523.webp?size=20","alt":"Galaxy Cruiser"},null],"Galaxy Cruiser"]},null]},null],["$","li","Planet Hopper",{"children":["$","a",null,{"href":"/d3b8aa65ffe6c","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/d3b8aa65ffe6c.webp?size=20","alt":"Planet Hopper"},null],"Planet Hopper"]},null]},null],["$","li","Space Taxi",{"children":["$","a",null,{"href":"/1ff1991efe029","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/1ff1991efe029.webp?size=20","alt":"Space Taxi"},null],"Space Taxi"]},null]},null],["$","li","Star Destroyer",{"children":["$","a",null,{"href":"/f3d9a88e1c234","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/f3d9a88e1c234.webp?size=20","alt":"Star Destroyer"},null],"Star Destroyer"]},null]},null],["$","li","Interceptor",{"children":["$","a",null,{"href":"/cb03cc4e5717e","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/cb03cc4e5717e.webp?size=20","alt":"Interceptor"},null],"Interceptor"]},null]},null],["$","li","Stealth Cruiser",{"children":["$","a",null,{"href":"/6c86fca8b9086","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/6c86fca8b9086.webp?size=20","alt":"Stealth Cruiser"},null],"Stealth Cruiser"]},null]},null],["$","li","Battleship",{"children":["$","a",null,{"href":"/fdc13cb488bf1","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/fdc13cb488bf1.webp?size=20","alt":"Battleship"},null],"Battleship"]},null]},null],["$","li","Dreadnought",{"children":["$","a",null,{"href":"/d486d48b82b81","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/d486d48b82b81.webp?size=20","alt":"Dreadnought"},null],"Dreadnought"]},null]},null],["$","li","Cruiser",{"children":["$","a",null,{"href":"/cfd10fcd2de6c","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/cfd10fcd2de6c.webp?size=20","alt":"Cruiser"},null],"Cruiser"]},null]},null],["$","li","Frigate",{"children":["$","a",null,{"href":"/e92cefe4f6727","style":{"fontWeight":"normal"},"children":[["$","img",null,{"src":"/img/ships/e92cefe4f6727.webp?size=20","alt":"Frigate"},null],"Frigate"]},null]},null],["$","li","Scout Ship",{"children":["$","a",null,{"href":"/ec7a3f950f99f","style":{"fontWeight":"bold"},"children":[["$","img",null,{"src":"/img/ships/ec7a3f950f99f.webp?size=20","alt":"Scout Ship"},null],"Scout Ship"]},null]},null]]

ui updates 🧭

more problems πŸ˜–

Pending UI

History

Caching

* aka react router v7

*

actions ⚑

... maybe next time πŸ˜…

🌟

🌟

🌟

🌟

🌟

🌟

🌟

🌟

🌟

And Now You Understand React Server Components

(hopefully)

You are fantastic

Thank you!

𝕏 @kentcdodds

And Now You Understand React Server Components

By Kent C. Dodds

And Now You Understand React Server Components

You want to keep up with the future of React. React has evolved over the years and continues to push the component model further and further. Out of all the evolutions of React, server components are certainly the biggest advancement. It expands the component model further than ever before and as a result, requires some rethinking. Seasoned React developers need to unlearn the way we used to do things to be able to understand the improvements that React Server Components offer. In this talk, Kent will guide you through React Server Components start to finish so you can understand how React Server Components work and set you off on your journey into the future of components everywhere!

  • 1,836