Performance
Speed is a feature. Performance is the foundation of user trust. Latency kills flow.
Measure reliably
You can't fix what you can't measure. Guesswork leads to optimisation of the wrong things.
Test on real conditions
Development machines lie. A fast laptop on fibre hides the problems your users face.
Do
Disable extensions. Use CPU and network throttling to simulate real devices. Profile in an incognito window.
Don’t
Profile on a high-end machine with a fibre connection and assume it's fine.
Core Web Vitals
Three metrics that define real-world user experience. Target green on all three.
No layout shift (CLS)
Stability is quality. Things should not jump around as they load.
Do
Set explicit dimensions on all images and media. Reserve space for content that loads asynchronously.
Don’t
Let an image push text down the page when it finally loads. Inject content above the viewport without reserving space.
Fast first paint (LCP)
Prioritise the viewport. Bandwidth should be spent on what is seen immediately.
<!-- Preload above-the-fold images --><link rel="preload" href="/hero.jpg" as="image" /><!-- Lazy-load everything else --><img src="footer.jpg" loading="lazy" alt="..." />
Do
Preload above-the-fold images. Lazy-load everything below the fold.
Don’t
Lazy-load the Largest Contentful Paint image, causing a visible delay on the most important element.
Responsive to input (INP)
The main thread is for UI. Heavy logic belongs elsewhere.
Do
Move expensive calculations or data processing to Web Workers. Break long tasks into smaller chunks with requestIdleCallback or scheduler.yield().
Don’t
Freeze the UI while parsing a large JSON file or running an expensive sort on the main thread.
Rendering
Reactivity has a cost. The DOM is slow. Minimise the work the browser has to do.
Track re-renders
Unnecessary renders burn battery and block interaction.
Do
Use React DevTools or React Scan to identify and minimise re-renders. Colocate state near where it's used.
Don’t
Assume functional components are cheap enough to re-render constantly. A parent re-rendering cascades to every child.
Minimise layout work
Reading and writing layout properties triggers expensive reflows.
Do
Batch DOM reads and writes. Animate with transform and opacity (compositor-only properties) instead of width, height, or top.
Don’t
Read a width, set a width, then read it again in a loop (layout thrashing).
Virtualise large lists
DOM nodes are heavy. Don't render what the user can't see.
/* Use virtualisation or CSS containment */<VirtualList items={items} />/* Or use CSS containment for simpler cases */.offscreen { content-visibility: auto; }
Do
Use virtualisation (e.g., virtua) or content-visibility: auto for long lists and large tables.
Don’t
Render 10,000 rows in a table just because the API returned them.
Network
Latency kills flow. API interactions should feel instantaneous.
Network budgets
Set expectations for how fast things should be.
Do
Ensure POST/PATCH/DELETE requests complete in <500ms. Use optimistic UI for fast, reversible actions so the interface moves at the speed of thought.
Don’t
Block the UI for 2 seconds while waiting for a database write.
Preconnect to origins
Handshakes take time. Establish connections before you need the data.
<link rel="preconnect" href="https://cdn.oztix.com.au" /><link rel="dns-prefetch" href="https://api.oztix.com.au" />
Do
Use <link rel="preconnect"> for critical CDNs or API domains.
Don’t
Wait until JavaScript requests a font before opening the connection to the font server.
Fonts
Typography should not block reading. Text must appear fast. See Typography for the full type system.
Optimise font loading
Custom fonts are a luxury the user pays for with load time.
Do
Preload critical fonts. Subset fonts to only include used characters (e.g., unicode-range). Use font-display: swap so text is visible immediately.
Don’t
Load a 5MB font file containing every language in the world for an English site.
What Roadie handles
The design system already takes care of several performance concerns. You get these for free.
Fluid typography
Sizes text-lg and above use clamp() for fluid scaling. No manual breakpoint overrides needed.
CSS-only utilities
Intent, emphasis, and interaction utilities are pure CSS. No JavaScript runtime cost for styling.
Tree-shakeable components
Components are built with tsdown in unbundle mode — every leaf compiles to its own dist file, so subpath imports only drag in what you use.
Reduced motion
A global prefers-reduced-motion reset neutralises all transitions and animations automatically.
Quick reference
How performance rules map to existing foundation pages.
| Rule | Foundation | What it covers |
|---|---|---|
| No CLS | Layout | Grid defaults, explicit sizing, gap over margin |
| Font optimisation | Typography | Fluid scaling, font families, preloading |
| Compositor animations | Motion | Transform and opacity transitions, motion tokens |
| Reduced motion | Motion | Global prefers-reduced-motion reset |
| Optimistic UI | Interactions | Loading states, feedback patterns |