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.

<!-- Set explicit dimensions on images -->
<img src="hero.jpg" width={800} height={400} alt="..." />
<!-- Reserve space for async content -->
<div style={{ minHeight: 200 }}>
{content ?? <Skeleton />}
</div>

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.

RuleFoundationWhat it covers
No CLSLayoutGrid defaults, explicit sizing, gap over margin
Font optimisationTypographyFluid scaling, font families, preloading
Compositor animationsMotionTransform and opacity transitions, motion tokens
Reduced motionMotionGlobal prefers-reduced-motion reset
Optimistic UIInteractionsLoading states, feedback patterns