Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 11.6Intermediate ~17 min · shot 6 of 7

View Transitions in React

In one line

React's own <ViewTransition> component is not in the stable React 19.2.8. What works today is document.startViewTransition with flushSync inside the callback, so React has rendered before the browser takes the new snapshot.

0/7 · 0%

Think of it like this

A photographer must take the "after" picture only when the room has been rearranged. If the movers are still carrying the sofa, the "after" looks like the "before". In React the movers are the renderer: wait for it before the browser takes its picture.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

flushSync
A react-dom function that makes React render and commit the update inside it before it returns.
commit
The moment React writes its result into the real DOM.
canary channel
React's pre-release channel for features not yet in a stable release.
race
A bug that depends on which of two things finishes first, so it works or fails from run to run.

Step by step

01Check what the installed React exports

Before trusting an article, look at the version you use:

terminal
$ node -e "console.log(Object.keys(require('react')).filter((k) => /transition|view|unstable|Activity/i.test(k)))"
── expected output ──
[
'Activity',
'startTransition',
'unstable_useCacheRefresh',
'useTransition'
]

02Flush inside the callback

Press Reverse (flushSync) and slow the timeline: four group(item-…) tracks appear and the items travel.

03See the race

Press Reverse (startTransition) a few times: the page text says how many items moved, mostly 4 and now and then 0. The console prints only the flushSync line, because it is the stable one.

04Press Add

Add (plain setState) appends an item with no transition and no tracks: only changes you route through startViewTransition animate.

Try it yourself

  1. 1

    Take the 40 ms away

    Delete slow(40) from the list. In eight runs startTransition then showed 4 of 4 every time: fine in a small example, now and then wrong with a heavy list.

Code & diagrams

flushSync inside the transition callback

Names come from state. The page text shows how many items moved in each case; the startTransition result varies from run to run.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

with flushSync: 4 of 4 items move

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Let React render later, on a heavy list

Replace flushSync(reverse) with startTransition(reverse) and keep slow(40).

terminal
$ Press Reverse and read the page text, in six separate loads of the page
── what you'll see ──
Four loads: `4 of 4 items move`. Two loads: `0 of 4 items move`, the list just flipped with no animation.

Myth vs fact

Myth

React handles view transitions for me.

Fact

Not in stable React 19.2.8: the component exists only in canary builds. The browser API plus flushSync is the stable way.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    flushSync forces a synchronous render, normally something to avoid. Inside a transition callback it is the point: the browser is already waiting. Keep the change small (a render slower than about 4 seconds loses the animation, Topic 11.1), and call it from handlers and the callback, not from effects or renders.

  • ▸

    Motion's layout and AnimatePresence animate inside React's lifecycle and never freeze the page, so they suit shared elements, enter and exit, gestures and springs (Phase 15).

Remember this

  1. 1

    The problem this solves: startViewTransition(update) expects the DOM to be changed when update returns. React does not change the DOM on setState: it schedules a render. So startViewTransition(() => setItems(next)) can take the new picture before React has drawn anything, and nothing animates.

  2. 2

    What exists in React 19.2.8. The installed package's exports include Activity, startTransition, useTransition and unstable_useCacheRefresh, and **no ViewTransition**: the component exists only in the canary and experimental channels, so do not import it in production code. Topic 11.6 of the React course (/react) says the same.

  3. 3

    The pattern. flushSync(() => setItems(next)) from react-dom makes React render and commit before it returns. Put it inside the update: document.startViewTransition(() => { flushSync(() => setState(next)); }). Name the elements from state: style={{ viewTransitionName: "item-" + id }}.

  4. 4

    **Without flushSync it is a race.** We reversed four items, each render taking 40 ms. With flushSync, 4 of 4 items moved in every run. With startTransition(reverse) (React renders in slices, later) four of six runs showed 4 of 4 and two showed 0 of 4 (the two pictures were identical). With the 40 ms removed, all eight runs showed 4 of 4. A plain reverse() in the callback also showed 4 of 4 in all 13 runs here, but React does not promise when it renders then, so only flushSync is safe. A race hides until the page gets heavy.

  5. 5

    Only changes you route through the helper animate. A plain setItems (the Add button) just updates: no snapshot, no paused rendering, no blocked clicks. For motion inside components (shared elements that stay mounted, enter and exit, gestures, springs) Motion's layout and AnimatePresence (Phase 15) work within React's lifecycle. Use view transitions for whole-view changes.

Explain it without notes

01

Why does document.startViewTransition(() => setState(next)) not reliably animate in React, and does React 19.2.8 export <ViewTransition>?

02

When would you reach for Motion instead of the browser API?

Practice

01

Write an animate(change) helper for a React app that falls back when the API is missing, respects reduced motion and never loses the state change.

02

Name the cards of a product grid from their ids so a sort animates, and say what key={index} would break.

Done when you can

  • You can say what React 19.2.8 exports for view transitions and how to check

  • You can write the flushSync pattern with a fallback

  • You can name list items from state and say why index names fail