cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

The rendering pipeline

Learn how browsers turn DOM changes into pixels through style, layout, paint, compositing, and smooth transform or opacity animation.

By the end, you can
  • 01
    Name each rendering stageExplain what style, layout, paint, and compositing contribute when a browser draws a page.
  • 02
    Avoid needless layout workRecognize a forced layout read and group reads before writes to avoid layout thrashing.
  • 03
    Choose animation-friendly propertiesUse transform and opacity deliberately while knowing that browser and page details still matter.

From DOM to pixels

A browser does not draw a DOM node directly. It works through a rendering pipeline: it works out which rules apply, measures boxes, draws pixels, and puts finished surfaces together on screen.

Definition

The rendering pipeline is the browser work that turns DOM and CSS into visible pixels: style, layout, paint, and composite.

These stages are connected, but they are not a promise that every change runs every stage. A text color change does not normally move a box. A width change can move many boxes. The property you change helps the browser decide what must be updated.

This follows From HTML to running script. That lesson explains how a document and its scripts arrive. This lesson begins once JavaScript or user input changes the page that the renderer will draw.

Style, layout, paint, and composite

Style calculation finds the CSS values that apply to an element. It combines browser defaults, your selectors, inherited values, and inline styles into values such as a color, display mode, width, and transform.

Layout measures boxes and places them. It answers questions such as how wide a card is, where its text wraps, and where the next card begins. Layout is also called reflow in many performance tools.

Change one style inputPop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");box.style.width = "120px";document.body.append(box);console.log(box.offsetWidth);

Line 1 creates a box. Line 2 gives it a width. Line 3 attaches it to the document. Line 4 reads its measured width and prints 120. The read is useful because it connects a CSS value to a real layout result.

Paint draws pixels for visible parts: text, backgrounds, borders, shadows, and images. Compositing puts painted surfaces in their final order, clips them, and applies properties such as transforms and opacity.

Real-life analogyDecorating a room

When you decorate a room, you choose colors first, measure where furniture goes, paint what people see, then stack glass sheets with pictures so one can slide over another.

In real life: Choose the colors
In JavaScript: Style chooses applicable CSS values
In real life: Measure and place furniture
In JavaScript: Layout decides box geometry
In real life: Paint walls and pictures
In JavaScript: Paint draws pixels
In real life: Slide glass sheets together
In JavaScript: Composite arranges prepared layers

Where the analogy stops: A browser has many more rules than a room. The analogy only gives the order of the visible jobs.

Forced synchronous layout

Browsers try to delay rendering work so several changes can be handled together. A style write can mark layout dirty. That is normally helpful because the browser can wait until the next frame instead of measuring after every single change.

A forced synchronous layout happens when JavaScript asks for current geometry after making a change that may affect geometry. Values such as offsetWidth, getBoundingClientRect(), and some computed styles need an up-to-date answer.

Read the width immediately after writing itPop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");box.style.width = "120px";document.body.append(box);console.log(box.offsetWidth); // 120

Line 1 creates the element. Line 2 writes 120px. Line 3 makes the box part of the page. Line 4 asks for a number now, so the browser returns 120 after resolving the needed layout work.

This is not automatically a bug. A drag handle, a tooltip, or a positioning calculation may truly need current geometry. It becomes a problem when code makes the browser repeat that work many times in one update.

Step through a style write and geometry read
Step 0 of 5Ready
Your turn: follow the blue line

Step through a compact teaching model of a style write followed by a layout read. The model runs real lesson functions and deliberately counts only the layout decisions it teaches.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
box.style.width = "120px";document.body.append(box);const width = box.offsetWidth;console.log(width);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Read and write without thrashing

Layout thrashing is an avoidable pattern: write a style, read geometry, write another style, then read geometry again. Each read can demand that the browser catches layout up before the loop continues.

Alternating a geometry read and style writePop out in the code editor (opens in a new tab)JavaScript
const cards = [...document.querySelectorAll(".card")];for (const card of cards) {  card.style.width = `${card.offsetWidth + 10}px`;}

Line 1 collects cards. Line 3 reads offsetWidth and writes a new width in the same expression. With a larger list, the next card's read can force layout after the previous card's write. The exact browser work varies, but the pattern creates unnecessary pressure.

Read first, then writePop out in the code editor (opens in a new tab)JavaScript
const cards = [...document.querySelectorAll(".card")];const widths = cards.map((card) => card.offsetWidth);for (const [index, card] of cards.entries()) {  card.style.width = `${widths[index] + 10}px`;}

Line 2 reads the old widths before any new width is written. Lines 3 through 4 then make all writes together. The browser can usually process the grouped update at the next rendering opportunity instead of answering a fresh geometry question per card.

Step through alternating and grouped work
Step 0 of 6Ready
Your turn: follow the blue line

Replay an instrumented teaching model that compares alternating reads and writes with grouping reads before writes.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const second = countLayouts(["read", "read", "write", "write", "frame"]);console.log(first);console.log(second);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Playground: reorder reads and writes
The layout counterJavaScript
const first = countLayouts(["write", "read", "write", "read"]);const second = countLayouts(["read", "read", "write", "write", "frame"]);console.log(first);console.log(second);
Order and result2 layouts
1. Write stylewrite

Marks layout dirty.

2. Read geometryread

Needs current geometry.

3. Write stylewrite

Marks layout dirty.

4. Read geometryread

Needs current geometry.

Try it yourself
Reorder operations below.

This teaching model counts 2 layout decisions. Put reads before writes, then use the final frame to process grouped writes once.

This compact model counts when a dirty style write must be resolved for a geometry read or the next frame. A real browser has more invalidation rules.

Layers and compositing

A layer is a separately composited surface the browser may create for an element or part of the page. A layer lets the compositor move, clip, or fade already-painted pixels without necessarily asking layout or paint to do the same work again.

For example, an element being transformed may be placed on a useful layer. will-change: transform is a hint that an element is likely to change. A transform animation can also lead the browser to prepare the right surface.

Hint that a card will movePop out in the code editor (opens in a new tab)CSS
.card {  will-change: transform;}

Line 1 chooses the card. Line 2 gives the browser a hint about a future transform. The code does not promise a layer, and it does not make every animation fast. It only gives the browser information it may use.

Too many layers waste memory because each surface needs backing storage. They can also add compositing work. Use a hint near an interaction you measured, then remove it when it no longer describes the page.

What triggers reflow and repaint

Property names provide a useful first guess. A change to width, height, top, or a font can change geometry, so the browser generally needs style, layout, paint, and composite work. The word reflow is another name for layout.

A small property-to-work guidePop out in the code editor (opens in a new tab)JavaScript
const changes = [  ["width", "layout, paint, composite"],  ["background-color", "paint, composite"],  ["transform", "often composite only"],];console.log(changes.map(([property, work]) => `${property}: ${work}`).join(" | "));

Line 1 creates three property examples. Lines 2 through 4 pair each one with likely work. Line 5 prints width: layout, paint, composite | background-color: paint, composite | transform: often composite only.

Common changes and their likely rendering work
Changed propertyLikely workWhy
width, height, top, font changesStyle -> layout -> paint -> compositeSize or position can change, so surrounding geometry must be considered.
color, background-colorStyle -> paint -> compositeThe geometry can stay the same, but pixels need new paint.
transform, opacityStyle -> often composite onlyIn most browsers these can often move or fade an existing layer without new layout or paint.
will-change: transformA hint, not a promiseIt can help prepare a layer, but too many promoted layers use memory.

Color and background-color normally keep the same geometry but need new pixels, so they repaint. Transform and opacity can often be composite-only in most browsers, but filters, blending, clipping, descendants, and browser choices can change the real result.

Playground: choose one changed property
Likely stageswidth

style -> layout -> paint -> composite

Use this result to form a hypothesis, then confirm it in browser DevTools.

Try it yourself
Changed property

width follows this useful rule of thumb: style -> layout -> paint -> composite.

This is a teaching guide. Browser versions, element contents, and effects can add work, so profile a real page before drawing conclusions.

Off-main-thread animation

The main thread runs JavaScript, event handlers, much of style and layout, and other page work. The compositor can often keep a prepared transform or opacity animation moving while a busy main thread is temporarily doing JavaScript.

This is why CSS transitions, CSS animations, and the Web Animations API are good fits for movement and fading when they animate transform or opacity. It is an opportunity, not a guarantee: keep the animated content simple and check a trace.

Start a Web Animations API transformPop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");box.style.width = "40px";box.style.height = "40px";document.body.append(box);box.animate(  [{ transform: "translateX(0px)", opacity: 1 }, { transform: "translateX(80px)", opacity: 0.5 }],  { duration: 300, fill: "forwards" },);console.log("animation started");

Lines 1 through 4 create a visible 40-pixel box. Lines 5 through 8 start an animation from one transform and opacity value to another. The last line prints animation started, proving the example started without claiming a timing result.

For an everyday guide to moving interface elements, see animation. For CSS property changes, see styles and classes and sizes and coordinates.

Browser evidence

The important observable fact is small: after a browser page writes a width and then reads geometry, the read reflects that new width. This does not reveal an engine's private implementation, but it proves that JavaScript receives current geometry when it asks.

Read geometry and computed stylePop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");box.style.width = "120px";document.body.append(box);console.log(box.offsetWidth);console.log(getComputedStyle(box).width);

Line 1 makes a box. Line 2 sets its width. Line 3 attaches it. Line 4 prints 120; the following computed-style version prints 120px. The lesson test runs these stable facts in Chrome when Chrome can start and skips the evidence check otherwise.

Read the computed widthPop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");box.style.width = "120px";document.body.append(box);console.log(getComputedStyle(box).width);

Do not turn this fact into a clock claim. Browser timings depend on the device, document, and frame schedule. The stable lesson claim is only that the read reflects the style update.

Practical rendering habits

Start with the visible problem. If typing, dragging, or scrolling feels uneven, record a performance trace before changing properties. Look for long JavaScript tasks and repeated style, layout, paint, or composite work around the interaction.

Then reduce unnecessary work. Keep DOM changes small, read geometry before writing new geometry, and prefer transform or opacity for motion when that matches the design. These are hypotheses to test, not rules to apply blindly.

  • Read layout values together before writing new layout-affecting styles.
  • Use transforms for movement and opacity for fading when the visual result is correct.
  • Use will-change for a measured, temporary reason rather than everywhere.
  • Inspect the browser trace; do not guess from one property name alone.

Connect this lesson with rendering performance, which applies these ideas to everyday page speed, and rendering and the event loop, which explains when the browser gets a chance to paint.

Sort each update by likely work
  • Change width from 120px to 160px.
  • Change a heading's font-size.
  • Change background-color.
  • Change text color.
  • Animate transform: translateX(...).
  • Animate opacity from 1 to 0.5.
Try it yourself
0 of 6 correct

Sort the cards by the most useful first guess. Read the explanation after each choice because real pages can add extra work.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common misconceptions

  • “Every DOM change lays out the whole page immediately.” Browsers often defer work until it is needed or until a frame.
  • “Reading geometry is always bad.” A current measurement is sometimes necessary; repeated read-after-write loops are the concern.
  • “`will-change` makes anything fast.” It is a hint with memory cost, not a permanent optimization switch.
  • “Transform is always free.” It can often avoid layout and paint, but large layers and visual effects still cost work.
  • “Off-main-thread means JavaScript is ignored.” A compositor animation may continue, but long JavaScript tasks still hurt input and other page work.
Rendering words that are easy to blur together
IdeaWhat it meansDo not confuse it with
Layout readAsks for current geometry such as offsetWidth.A style write; reads can make pending layout happen now.
PaintDraws pixels for text, borders, backgrounds, and images.Compositing; composition arranges already-painted layers.
LayerA separately composited surface the browser may use.A guarantee that an element never paints again.
Composite-only candidateA transform or opacity update that can often use existing pixels.A universal performance promise; page details and browsers still matter.

Practice exercises

Exercise 1 · Warm-upPredict the measured width

Type the number printed by the small browser example.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");
box.style.width = "75px";
document.body.append(box);
console.log(box.offsetWidth);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upCount alternating work

    Predict the count from the compact layout model.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function countLayouts(steps) {
      let dirty = false;
      let layouts = 0;
      for (const step of steps) {
        if (step === "write") dirty = true;
        if ((step === "read" || step === "frame") && dirty) {
          layouts += 1;
          dirty = false;
        }
      }
      return layouts;
    }
    console.log(countLayouts(["write", "read", "write", "read"]));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeBatch your measurement

      A list is resized after you measure its cards. Which kind of operation should you group before making style writes?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeChoose a motion property

        Which property would you choose first to slide a panel sideways without changing its document-flow position?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeApply the idea to a real website

          A checkout page stutters while a coupon panel opens. What should you inspect before adding `will-change` to every card?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeChoose the sliding implementation

            Your app slides a help panel in from the right. Which update would you try first?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              Use the pipeline names precisely, then separate a property rule of thumb from a measured result on a real page.

              Rendering pipeline quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What is layout in the rendering pipeline?

                Choose an answer to see the explanation.

              2. Question 2 of 7What does this browser code print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const box = document.createElement("div");
                box.style.width = "120px";
                document.body.append(box);
                console.log(box.offsetWidth);

                Choose an answer to see the explanation.

              3. Question 3 of 7Why can a read after a style write be expensive?

                Choose an answer to see the explanation.

              4. Question 4 of 7What does the teaching model print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                let dirty = false;
                let layouts = 0;
                for (const step of ["write", "read", "write", "read"]) {
                  if (step === "write") dirty = true;
                  if (step === "read" && dirty) { layouts += 1; dirty = false; }
                }
                console.log(layouts);

                Choose an answer to see the explanation.

              5. Question 5 of 7Which statement about layers is accurate?

                Choose an answer to see the explanation.

              6. Question 6 of 7Which properties can often animate off the main thread?

                Choose an answer to see the explanation.

              7. Question 7 of 7What is the practical first response to visible jank?

                Choose an answer to see the explanation.

              Key takeaways

              • Style finds applicable CSS values, layout decides geometry, paint draws pixels, and composite arranges prepared surfaces.
              • A geometry read after a relevant style write can force the browser to resolve layout now.
              • Group reads before writes to avoid repeated layout decisions in one update.
              • Layers can help compositing, but too many layers consume memory.
              • Transform and opacity can often animate smoothly through compositing in most browsers.
              • Profile a real interaction before choosing an optimization.

              Remember the one-liner.
              The rendering pipeline turns page state into pixels, and the property you change decides how much work that update is likely to need.

              Coming next: How input reaches your event handlers. You will follow a pointer, touch, or keyboard action from hit testing to the JavaScript listener that receives it.

              CompleteFrontend Clear concepts. Working examples.