# The Rendering Pipeline > Style → Layout → Paint → Composite: what triggers each stage. Frontend architecture guidance from fearchitect, written by Abas Turabli and last reviewed 2026-06-21. Source: https://fearchitect.com/topics/rendering-pipeline Use it as reference for the task at hand. Before changing code, check this guidance against the codebase: where the code already makes a different, deliberate choice, flag the conflict instead of rewriting it. Library APIs move faster than this guide, so confirm exact signatures in the official docs linked at the end. ## Summary The browser renders a frame by running up to four stages: Style (match CSS rules), Layout/reflow (compute geometry), Paint (record draw calls), and Composite (GPU layer merge). Triggering Layout is the most expensive path; CSS properties like `transform` and `opacity` skip it entirely and run on the compositor thread, costing far less than a full reflow. ## Diagram ```mermaid flowchart LR A[Style] --> B[Layout / reflow] B --> C[Paint] C --> D[Composite] B -.->|"skip paint+layout e.g. color change"| C C -.->|"skip layout e.g. opacity / transform"| D ``` Full pipeline: Style → Layout → Paint → Composite. `opacity` and `transform` skip to Composite, bypassing the two most expensive stages. ## What each stage does ### 1. Style The browser matches every CSS rule against the DOM and computes the final computed style for each element. Triggered on any class or attribute change that alters applied rules. ### 2. Layout (reflow) Geometry is computed: position, width, height, and margin for every affected element and its descendants. Changing `width`, `height`, `top`, `margin`, or `padding` starts here. Reflow is the most expensive stage on a large DOM. ### 3. Paint The browser records draw calls (fills, strokes, shadows, text) into a display list per layer. Triggered by color, background, box-shadow, or border changes — but not geometry shifts. ### 4. Composite The GPU merges painted layers in the correct stacking order and sends the finished frame to the screen. `transform` and `opacity` animate here without touching the main thread. ## CSS property cost by pipeline stage | Property change | Triggers | Thread | | --- | --- | --- | | **width / height / margin** | Style + Layout + Paint + Composite | Main | | **color / background-color** | Style + Paint + Composite | Main | | **transform / opacity** | Composite only | Compositor (GPU) | | **box-shadow / border-radius** | Style + Paint + Composite | Main | ## Watch out: Layout thrash Reading a geometry value (`offsetWidth`, `getBoundingClientRect()`) after a DOM write forces the browser to flush pending layout synchronously. Inside a loop this multiplies reflow cost by iteration count. Batch all reads before writes, or defer writes to `requestAnimationFrame`. ## Compositor-layer rules of thumb - Animate `transform` and `opacity` — they skip the main thread entirely. - Use `will-change: transform` only on elements that will animate; excess layers waste GPU memory. - Each `will-change` or `transform` creates a stacking context — watch for z-index surprises. - Profile with Chrome DevTools Performance panel before promoting layers speculatively. - Avoid animating `top`/`left` with JS; use `translate()` instead to stay on the compositor. ## Key terms - **reflow**: Browser recalculating geometry of every affected element; triggered by dimension or position changes. - **layout thrash**: Alternating DOM reads and writes inside a loop, forcing repeated synchronous reflows. - **compositor thread**: GPU-side thread that merges pre-painted layers; runs independently of the main thread. - **will-change**: CSS hint that promotes an element to its own compositor layer before animation starts. - **stacking context**: Isolated z-order subtree created by properties like `opacity < 1`, `transform`, or `isolation: isolate`. ## Related topics - [Paint & Composite Optimization](https://fearchitect.com/topics/paint-composite-optimization.md): Animate only transform and opacity to skip paint entirely. - [Event Loop & scheduler.yield()](https://fearchitect.com/topics/event-loop-scheduler-yield.md): Break long tasks to keep the main thread responsive and hit INP. - [Core Web Vitals](https://fearchitect.com/topics/core-web-vitals.md): Google's three user-experience metrics: LCP, INP, and CLS. - [Render Performance](https://fearchitect.com/topics/render-performance-patterns.md): Skip renders, defer slow work, and virtualize long lists. - [Web Workers & Off-Main-Thread](https://fearchitect.com/topics/web-workers-off-main-thread.md): Offload CPU-heavy work to a background thread, keeping the main thread free. - [WebAssembly on the Frontend](https://fearchitect.com/topics/webassembly.md): Portable bytecode that runs near-native in a sandboxed browser VM. ## Further reading - [web.dev — Rendering performance overview](https://web.dev/articles/rendering-performance) - [web.dev — Avoid large, complex layouts and layout thrashing](https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing) - [web.dev — Stick to compositor-only properties and manage layer count](https://web.dev/articles/stick-to-compositor-only-properties-and-manage-layer-count) - [MDN — CSS will-change](https://developer.mozilla.org/en-US/docs/Web/CSS/will-change)