Topic 19.7
Document Metadata and Resource Loading
In one line
In React 19 any component can render <title>, <meta> and <link> and React places them in the document <head>; stylesheets with a precedence and async scripts are inserted once in the right order, and preload, preinit, preconnect and prefetchDNS from react-dom tell the browser early about resources it will need.
Think of it like this
The front door of a house with a name plate and a doorbell. Every room is decorated by whoever lives in it, but the name plate must be on the front door, not in the bedroom. React 19 lets each room (component) say "the name plate should read *Asha's Profile*", and React carries it to the front door (<head>). Resource hints are like telling the delivery company in the morning "a parcel will come from this shop later", so the van is already on its way when you need it.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Document head
- The
<head>part of the page: title, meta tags, stylesheets and other information about the page. - Metadata
- Information about the page rather than visible content, such as the tab title or a description for search engines.
- Hoisting
- React moving
<title>,<meta>and<link>from where you render them into<head>. - precedence
- A prop on stylesheets that tells React where to put them in
<head>and lets it insert each one once. - De-duplication
- Inserting a resource only once even when many components ask for it.
- Resource hint
- A tag telling the browser early about a server or file it will need, so it can start work sooner.
- preconnect
- Open the network connection to a server early, before any request is made.
- preload / preinit
- Download a file early (preload) or download and apply/run it now (preinit).
Step by step
01The old way: an effect
Before React 19, setting the title meant an effect. It ran after the paint, didn't work in server rendering, and every page had to remember it.
useEffect(() => {
document.title = user.name + " | Hectal";
}, [user.name]);02React 19: render the tag
Render the metadata next to the content it describes. React moves it into <head> and removes it when the page component unmounts.
function ProfilePage({ user }) {
return (
<main>
<title>{user.name + " | Hectal"}</title>
<meta name="description" content={"Profile of " + user.name} />
<link rel="canonical" href={"https://hectal.in/u/" + user.id} />
<h1>{user.name}</h1>
</main>
);
}03Where everything ends up
Hoisted tags go to <head>; ordinary content stays in your root. Stylesheets are grouped by precedence, so a component's CSS lands in a predictable place in the cascade.
04Start downloads early
When a component knows it will soon need a font, an image or a script, it calls a resource function during render. The browser starts the work while React is still rendering.
import { preconnect, preload, preinit } from "react-dom";
function VideoPage() {
preconnect("https://video.example.com");
preload("/fonts/inter.woff2", { as: "font", type: "font/woff2", crossOrigin: "anonymous" });
preinit("/player.js", { as: "script" });
return <Player />;
}Try it yourself
- 1
Unmount the page
In the first example, add a button that hides
<Page />completely. Predictdocument.titleafterwards. React removes the<title>it inserted, so the tab falls back to the document's own title (empty here). - 2
Swap the precedence
In the second example, give the purple
<link>precedence="default"and add ah1{color:orange}stylesheet withprecedence="low"rendered first. Predict the order in<head>and the finalh1colour. - 3
Remove async
Remove
asyncfrom the<script>tag. Predict: is it still hoisted and run once? (A plain script element created by React isn't executed by the browser at all, so nothing logs.)
Code & diagrams
The `<title>` and `<meta>` were rendered inside `<article>` but live in `<head>`. When the page changes, React updates the same title tag: `document.title` follows the state.
Preview
Console
Nothing logged yet.Expected console output
title: Home | Hectal
in head: true
meta: About Home
title: Profile | Hectal
titles in doc: 1Rendered twice, yet the script ran once and each link appears once. Stylesheets come first, grouped by precedence (`default` was seen first, then `high`), each with `data-precedence`. The resource hints follow. The cdn.example.com URLs are placeholders: nothing is fetched from them here.
Preview
Console
Nothing logged yet.Expected console output
widget script ran
<link rel="stylesheet" href="data:text/css,p{color:teal}" data-precedence="default">
<link rel="stylesheet" href="data:text/css,h1{color:purple}" data-precedence="high">
<link rel="preconnect" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://img.example.com">
<link rel="preload" href="https://cdn.example.com/font.woff2" as="font" crossorigin="" type="font/woff2">
<link rel="preload" as="style" href="data:text/css,h1{color:purple}">
<script async="" src="data:text/javascript,console.log('widget script ran')"></script>
h1 color: rgb(128, 0, 128)
p color: rgb(0, 128, 128)import { renderToString } from "react-dom/server";
function Page() {
return (
<main>
<title>Profile | Hectal</title>
<meta name="description" content="Profile of Asha" />
<link rel="stylesheet" href="/card.css" precedence="default" />
<h1>Asha</h1>
</main>
);
}
console.log(renderToString(
<html><head></head><body><div id="root"><Page /></div></body></html>,
));
// Output (one line, wrapped here):
// <html><head><link rel="stylesheet" href="/card.css" data-precedence="default"/>
// <title>Profile | Hectal</title><meta name="description" content="Profile of Asha"/>
// </head><body><div id="root"><main><h1>Asha</h1></main></div></body></html>Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Several children in <title>
In the first example write <title>{name} | Hectal</title> instead of building one string.
Break #2
A stylesheet without precedence
Remove precedence="high" from the purple <link> and render <Article /> twice.
Myth vs fact
Myth
You still need react-helmet to set the page title.
Fact
React 19 renders <title>, <meta> and <link> from any component and hoists them to <head>, on the client and the server.
Myth
preload runs the script.
Fact
preload only downloads. preinit downloads and executes (scripts) or applies (stylesheets).
Myth
Rendering the same stylesheet in many components loads it many times.
Fact
With precedence, React inserts it once per href. Async scripts are also de-duplicated by src.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
During streaming SSR, React puts hoistable elements and precedence stylesheets into the
<head>it sends first, and with Suspense it holds back revealing a boundary until the stylesheets that boundary needs have loaded. - ▸
On the client, React keeps a per-document cache of resources keyed by URL. That's what makes
preload/preinitand precedence stylesheets idempotent: calling them on every render is cheap. - ▸
Precedence groups are ordered by first appearance, not alphabetically. Teams usually define a fixed set (
"reset","default","high") and render the lowest one early in the app shell, so the cascade order stays predictable. - ▸
Frameworks layer their own metadata APIs on top (Next.js
metadataexports, Topic 20.4). Use those when available: they merge metadata from nested layouts and handle defaults.
Remember this
- 1
**The
<head>** is the part of an HTML page that holds information about the page rather than visible content: the<title>shown in the browser tab,<meta>tags (description, social previews),<link>tags (stylesheets, icons, the canonical URL). See the/htmlcourse. A React app usually mounts into a<div>in the<body>, so before React 19 components couldn't put things in<head>: you useduseEffect(() => { document.title = ... })or a library such asreact-helmet. - 2
Metadata in components. In React 19 you render
<title>,<meta>and<link>anywhere in your tree, and React hoists them (moves them up) intodocument.head. When the component unmounts they are removed. The page that's on screen decides its own title:<title>{product.name + " | Shop"}</title>. This works on the client and in server rendering, where the tags are streamed into the HTML<head>, which matters for search engines and link previews (Phase 20,/system-designfor caching). Exceptions: a<title>inside<svg>or with anitemPropstays where it is. - 3
**Give
<title>one string.**<title>{name} | Hectal</title>is an array of three children in JSX. On the server React warns that a title's children must be a string; in the browser the title ends up empty. Build the string first:<title>{name + " | Hectal"}</title>or a template literal. If two components render a<title>, both are put in<head>and the browser uses whichever comes first there (in a test with<title>One</title><title>Two</title>, the tab showedTwo), so render the title from one place per page (usually the route component). Frameworks such as Next.js have their own metadata API on top (Topic 20.4). - 4
**Stylesheets with
precedence.**<link rel="stylesheet" href="/card.css" precedence="default" />lets a component bring its own CSS. React inserts it into<head>once, however many components render it (de-duplicated byhref), orders sheets by precedence (sheets of a precedence that appeared first come first; a"high"group added later goes after them, so it wins in the cascade) and, on the client, waits for the sheet to load before showing the content that needs it, so you don't see unstyled content (a flash of unstyled content). Withoutprecedence, a<link>is rendered where you put it, like before.<style href="..." precedence="...">works the same for inline CSS. - 5
Async scripts.
<script async src="/widget.js" />rendered from a component is hoisted and de-duplicated: rendered by ten components, it loads and runs once. Scripts withoutasyncare not moved or de-duplicated. - 6
**Resource hints from
react-dom.** Call these during render or in event handlers; React inserts the matching<link>in<head>, once per URL.prefetchDNS(url): look up the domain's IP address early.preconnect(url): also open the connection (DNS, TCP, TLS) to a server you'll fetch from soon.preload(url, { as }): download a file you'll need soon (fonts, images, styles) without using it yet.preinit(url, { as: "style" | "script", precedence? }): download and apply/run a stylesheet or script now.preloadModuleandpreinitModuledo the same for ES modules. They speed up loading by starting downloads before the browser discovers the files itself; overusing them wastes bandwidth.
Explain it without notes
How do you set the page title per page in React 19, and how does it work?
What does the precedence prop on a stylesheet do?
Explain the difference between prefetchDNS, preconnect, preload and preinit.
Practice
Write a ProductPage that sets the title to <name> | Shop and a description meta tag.
In a MapView component, preconnect to https://tiles.example.com and preinit its stylesheet https://tiles.example.com/map.css with precedence default.
Trade-offs
- ↔
Colocating metadata with components is simple, but two components rendering
<title>compete and the winner depends on insertion order; keep one owner per page or use your framework's metadata API. - ↔
Resource hints speed up the critical path only when the resource is really needed soon; hints for unused files waste the user's bandwidth and can slow down more important downloads.
Done when you can
I can set
<title>and<meta>from a component and know they go to<head>.I give
<title>a single string child.I can load component stylesheets with
precedenceand know they are de-duplicated.I know async scripts are hoisted and loaded once.
I can choose between
prefetchDNS,preconnect,preloadandpreinit.