A page drawn on the server
Pinch or Ctrl+scroll to zoom; the text is selectable.
Source
·
TheYard
A page drawn on the server: from the address through the edge, the rendering service and the API to the HTML the browser paints, and then the takeover
A page drawn on the server: the first load, then the takeover
Every box is a file; the path under each title is where that step lives. The service in render/ is a client of the API, like a browser; the API draws nothing and does not know it exists.
The first load: drawn once, on the server, for this request
From the address to the painted page. The API is only read; a read that misses the deadline is left to the browser.
The takeover: React attaches, it does not redraw
Hydration, in React's word. One mismatch and the markup is thrown away.
The address
the visitor's browser
theyard.stevenstout.biz/?view=inventory, ?vehicle= or ?doc=. A page is the
bare path with its query; the bundle, the photographs and /api are other
paths, which never come this way.
GET /?view=inventory
The edge
edge/_redirects
Netlify matches the bare path on either name and rewrites it, query and all,
to the rendering service as that site: /site/sql or /site/cosmos. Everything
else goes to that site's API as before.
200 rewrite to /site/sql?view=inventory
The page and the build
render/page.ts
The service reads index.html and /api/version from the API it draws for,
kept fifteen seconds. The page names the bundle that API serves; the service
draws only when the API runs the build the service was made from, and during
a roll it sends the API's page untouched for the browser to draw.
the API runs the same build: draw
One loader per view, under one deadline
render/loaders.ts
Who is signed in (the cookie forwarded, nothing else) and the build for the
footer on every page; on an inventory or vehicle address the first page of
the list with its facets; on ?vehicle= the vehicle; on ?doc= the document,
rendered with the same Markdown code the browser uses. All at once under 2.5
seconds; a read that fails or runs late is left to the browser.
the reads, all at once
The draw, and the stream
render/render.ts, src/app/entry-server.tsx
Everything above #root leaves at once, before any read has answered, so the
browser starts on the stylesheet, the font and the bundle. Then #root with
the site drawn by React's server renderer from what the loaders read; then
the first load, that same data as JSON beside #root with every < escaped;
then the rest of the page.
the top first, then the drawing, then the first load
The browser paints
the visitor's browser
The HTML arrives with the view in it, and the browser shows the inventory,
the vehicle or the document before any script has run. Measured on 8
October: the inventory's speed index on a phone 3.5 s where it read 5.8
drawn in the browser.
The bundle arrives
index.html, the API's own page
The script the page named, already downloading while the
service was drawing. Nothing in it is special to a drawn page.
script runs
The first load is read
src/app/mount.tsx, src/app/firstLoad.ts
#root says data-drawn="server" and the JSON beside it says
which address it was drawn for. If it matches the address bar,
hydrateRoot; otherwise the page is drawn from nothing, as it
always was.
the address matches: hydrateRoot
Every hook starts from it
src/hooks/useFirstLoad.ts
Each hook that used to start empty (the address, the list, the
vehicle, the account, the build, the clock) starts from the
first load instead, so the browser's first draw is the
server's draw, byte for byte.
same markup on both sides
The four differences, answered
src/hooks/useMediaQuery.ts, src/library/DocDialog.tsx
The clock: the browser counts down from the time the server
drew at, then ticks. The time zone: UTC until the page is
live, then the viewer's. The window's width: "does not match"
until the page is live, then the real answer. The document
window: drawn open where the modal stands, then opened again
as a modal.
after the takeover, each changes once
Live
src/app/TheYard.tsx
React has attached its handlers to the markup it found. A
click, a filter, a bid: the site from here is the site it
always was, reading /api as before. tests/e2e/drawn.spec.ts
opens every drawn view in Chrome and fails on a hydration
error or a window that moved.
the page is on screen while the script is still arriving; the dashed line is the takeover beginning
Measured on the service alone in Docker: the top of the page in 7 to 9 ms, the drawn inventory in 105 ms at the median.
Through the domain the top and the drawing arrive together, because the edge in use today holds a proxied answer until it has all of it.
Source: docs/images/server-page.svg in the repository, drawn by docs/images/server-page.mjs and redrawn when a step moves.