(wish me luck)
Your brain needs this π§
build underscore.js from scratch
*Only official react, react-error-boundary, and hono.js
import { createElement as h } from 'react'
*It's not really a compiler, but a node loader (runtime module transformer)
Coming to EpicReact.devΒ soon
SPA
No SSG
No client router yet
// 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)
})
// ...
{
"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>...
You'll figure it out...
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]]
Pending UI
History
Caching
* aka react router v7
*
π
π
π
π
π
π
π
π
π
(hopefully)
π @kentcdodds