Optimizing UI Performance
1) What counts as "fast"
TTFB (time to first byte) - fast server/CDN response.
LCP (Largest Contentful Paint) - "main" content appeared quickly.
INP (Interaction to Next Paint) - responsiveness during interaction.
CLS (Cumulative Layout Shift) - absence of interface jitter.
TTI (Time to Interactive) - when everything is already responding.
Recommended reference points: LCP ≤ 2. 5s, INP ≤ 200ms, CLS ≤ 0. 1 (for the 75th percentile of real users).
2) Process: measure → find bottlenecks → fix budgets
1. Measure: RUM (real users, percentiles by country/network/device) + synthetics (Lighthouse/browsers).
2. Find: Performance profiler (long tasks> 50ms, layout thrashing, extra renders).
3. Fix: budgets (weight of JS/CSS/fonts, LCP/INP) and "red lines" in CI.
3) Network and resource loading
3. 1 HTTP and priorities
Enable HTTP/2/3, Brotli compression.
'preconnect'to critical domains;' dns-prefetch'to secondary domains.
'preload'for critical resources (hero image, main font).
'fetchpriority =" high/low"' and'priority' hints where supported.
3. 2 Caching
Static with file hash: 'Cache-Control: public, max-age = 31536000, immutable'.
HTML - short TTL + stale-while-revalidate via CDN.
ETag/Last-Modified and Service Worker for offline/repeat visits.
4) Code: less, later, "flatter"
4. 1 Assembly
Tree-shaking, minify (в т.ч. dead-code-elim).
Code-splitting by routes/widgets, dynamic import.
Avoid "global" heavy packets in the basic bundle (moment → Intl/Day. js).
4. 2 HTML Rendering and Delivery
SSR/ISR/streaming: give the framework and main content first.
Partial/Islands hydration: hydrate only interactive areas.
Defer all non-critical: '<script type = "module" defer>'.
4. 3 Reaction specificity (if React is used)
`React. lazy '+' Suspense 'for lazy widgets.
'startTransition'and'useDeferredValue' for heavy filters/lookups.
RSC (Server Components) - Server calculations, less than JS on the client.
Selectors in the (zustand/redux): sign the component into fragments, not the entire stack.
5) Render and state: where "slows down"
5. 1 Isolation of rerenders
Crush large components, memoize ('memo', 'useMemo', 'useCallback').
List keys are stable; do not create new functions/objects in the props unnecessarily.
Avoid "global" context for frequently changing data - use selectors or event buses.
5. 2 Virtualization and big lists
Sheets/tables → virtualization (render window).
Pagination/infinite scrolling with backpressure (do not load 100k lines at once).
Delayed initialization of heavy widgets outside the viewport.
5. 3 Layout & paint
content-visibility: auto; for hidden partitions (the browser does not render the invisible).
contain and 'contain-intrinsic-size' for predictable sizes.
Avoid frequent layout reads-entries (layout thrashing); group measurements.
will-change use dosed (otherwise extra memory/layers).
6) Images and graphics
Formats: AVIF/WebP (fallback on PNG/JPEG).
Responsive approach: 'srcset' + 'sizes', density-based for retina.
'loading =" lazy"' for non-hero images; priority/preload - for LCP candidate only.
Fixed size placeholders → no CLS jumps.
Canvas/charts: offscreen canvas and Web Worker for calculations; butching redraws.
7) Fonts and text
One or two variable font instead of many styles.
'font-display: swap '/' optional ', preload for basic style.
'size-adjust'to reduce the "jump" when changing the font.
Local fallback fonts with similar metrics.
8) CSS and animations
Critical CSS inline (<14-20 kB), rest - to be postponed.
Delete unused styles (Purge/CSSTree).
Animations, if possible, on transform/opacity; respect 'prefers-reduced-motion'.
Avoid deep cascades and blasting selectors.
9) Web Workers, Flow and Heavy Tasks
All CPU-heavy - in Worker (parsing, sorting, aggregation, ML).
Streaming APIs ('ReadableStream', 'fetch' with streams) for long responses.
Splitting tasks into chunks via 'requestIdleCallback '/microtasks to maintain responsiveness.
10) Layout Stability (CLS)
Reserve space for the LCP element (image/widget).
Do not insert banners/ribbons without fixed sizes.
Asymmetric tultips/toasts - do not move content; use layers/portals.
11) Examples of snippets
Critical font and LCP image
html
<link rel="preload" href="/fonts/Inter. var. woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" as="image" href="/hero. avif" imagesrcset="/hero. avif 1x, /hero@2x. avif 2x" fetchpriority="high">
Lazy and secure widget initialization
js const Widget = React. lazy(() => import('./Widget'));
function Section() {
const inView = useInViewport('#sec');
return <div id="sec">{inView? <React. Suspense fallback={null}><Widget/></React. Suspense>: null}</div>;
}
Layout stabilization
css
.hero {
content-visibility: auto;
contain: layout paint;
contain-intrinsic-size: 720px 320px ;/LCP reserve/
}
12) Regression control and budgets
Bundle-budget: total JS ≤ N kB, CSS ≤ M kB, initial-chunk ≤ K kB.
Web-Vitals in CI (emulated) + RUM-alerts (on percentiles).
Bundle analysis: source-map-explorer/analyzer in PR.
Performance benchmarks of components (render of 10k elements, reaction time).
13) Anti-patterns
Load "all at once": graphs, editors, maps in the first screen.
Huge global state → cascading rerenders.
Images 2-4 × of the desired size, without 'srcset/sizes'.
Long synchronous loops on the main thread.
'ouline: none'and custom tricks without optimization - interfere with render indicators.
Animations by 'top/left' (break the layout and call reflux).
14) Screen checklist
- LCP ≤ 2. 5 s on 3G/mobile traffic, CLS ≤ 0. 1, INP ≤ 200ms
- Critical resources: preload/priorities; the rest is defer/lazy
- Bundles: code-split, no extra dependencies
- List/Table Virtualization, Heavy Widget Delayed Initialization
- Images: AVIF/WebP,' srcset/sizes', 'loading =" lazy"'
- Fonts: variable + 'font-display', preload only desired
- CSS: critical inline, Purge, 'content-visibility' and 'contain' where appropriate
- Workers/idle for heavy computing
- Budgets and Web-Vitals are connected to dashboards/alerts
15) Implementation plan (3 iterations)
Iteration 1 - Quick Wins (1-2 weeks)
Enable Brotli/HTTP-2/3, CDN. Critical CSS and preload LCP resources.
Move heavy widgets to dynamic imports.
Images → AVIF/WebP + 'srcset'. Fonts: 'font-display: swap'.
Iteration 2 - Structural Improvements (3-4 weeks)
Code-split by route, bundle analysis, removal of "heavy" libs.
List virtualization, content visibility, contain-intrinsic-size.
Implement SSR/streaming/islands (where relevant).
RUM with Web-Vitals, budgets in CI.
Iteration 3 - Scale and Robustness (Continuous)
Workers/offscreen canvas, butching calculations, startTransition/deferredValue.
Regular perf audit, regression auto digest, team training.
16) Mini-FAQ
What will speed up the most on mobile?
Reducing the original JS, SSR/streaming and optimizing the LCP image.
Do I always need SSR?
No, it isn't. If the page is dynamically interactive and cached poorly, islands/partial hydration may be better.
Why is INP bad with a "light" bundle?
Probably long tasks (sorting, graphics) on the main thread - bring them to Worker and split the tasks.
Total
Fast UI is a collection of disciplines: network priorities and cache, smaller and late bundles, predictable non-jump rendering, economical images and fonts, and constant real-world metric control. Enter budgets, automate checks and teach the team to think about speed at every step - this way the interface will remain fast today and in a year.