[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-react-weekly-viewtransition-component":44,"search-suggestions":455,"related-react-weekly-viewtransition-component":490,"code:js:true:lomfj4":606,"code:jsx:true:460eoo":607,"code:jsx:true:1nso9nm":608,"code:css:true:1qq9siz":609,"code:jsx:true:1kc6j26":610,"code:jsx:true:d8uga5":611,"code:jsx:true:6swv1f":612,"code:css:true:1nyjtsk":613,"code:jsx:true:n0627k":614,"comments-01a0f615-d2be-750b-839e-02fd75291b4e":615},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":74,"playground":76,"body":78,"bodyMd":416,"seo":417,"translationGroupId":419,"series":420,"podcastUrl":48,"verticalId":5,"thread":443,"assessments":445,"translations":451,"quiz":453},"01a0f615-d2be-750b-839e-02fd75291b4e","react-weekly-viewtransition-component","React 19.3 ViewTransition: Animate State Without Losing It",null,"React 19.3's ViewTransition component animates state changes, not just page swaps — the mental model, enter\u002Fexit\u002Fupdate\u002Fshare, and a cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-weekly-viewtransition-component.png",15,"2026-10-03T11:22:26.437Z",39,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"react","React",[64,65,68,71],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},"javascript","Javascript",{"slug":69,"name":70,"color":48},"tutorial","Tutorial",{"slug":72,"name":73,"color":48},"webdev","Webdev",{"assessments":75},1,{"slug":46,"title":77},"React ViewTransition — interactive playground",{"blocks":79,"version":75},[80,84,87,90,95,98,107,110,113,116,129,132,135,141,144,147,150,153,158,161,164,183,186,189,193,196,201,204,207,210,214,217,220,223,227,230,233,236,239,242,245,248,251,254,258,261,265,268,271,280,283,290,293,297,300,303,306,310,313,316,319,322,325,328,331,334,337,340,343,347,366,369,377,380,383,386,389,392,398,401,404,407,410],{"id":81,"html":82,"type":83},"b1","\u003Cp>You wire up a photo grid. Click a thumbnail, it should morph smoothly into a full detail view — the kind of transition Google Photos and Instagram have trained everyone to expect. You already know the browser can do this for free: the native View Transitions API wraps a DOM update in \u003Ccode>document.startViewTransition()\u003C\u002Fcode>, tags the old and new elements with a shared \u003Ccode>view-transition-name\u003C\u002Fcode>, and the browser tweens between them. You wire it up. It works beautifully — right up until the thumbnail lives inside a filtered, paginated React list, and the click also has to update \u003Ccode>selectedId\u003C\u002Fcode> state, close a search box, and possibly wait on data that hasn&#39;t arrived yet. Now the animation glitches, fires on the wrong element, or doesn&#39;t fire at all, and nothing in the DevTools explains why.\u003C\u002Fp>","paragraph",{"id":85,"html":86,"type":83},"b2","\u003Cp>This is episode five of \u003Cstrong>React Deep Dive\u003C\u002Fstrong>, on what React itself decides rather than JavaScript with a React import. This one is about a decision the reconciler now makes explicitly: which DOM changes are worth animating, and which four shapes every one of those changes takes.\u003C\u002Fp>",{"id":88,"html":89,"type":83},"b3","\u003Cp>This article is written against \u003Cstrong>React 19.3\u003C\u002Fstrong> (verified against React&#39;s own \u003Ccode>CHANGELOG.md\u003C\u002Fcode> on GitHub, version 19.3.0, published September 9, 2026), where the \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> component and \u003Ccode>addTransitionType\u003C\u002Fcode> shipped as \u003Cstrong>stable\u003C\u002Fstrong>, not experimental. Everything here assumes React 19-era function components and hooks.\u003C\u002Fp>",{"id":91,"html":92,"text":93,"type":94,"level":31},"b4","What you&#39;ll learn","What you'll learn","heading",{"id":96,"html":97,"type":83},"b5","\u003Cp>By the end of this article you&#39;ll be able to:\u003C\u002Fp>",{"id":99,"type":100,"items":101,"ordered":18},"b6","list",[102,103,104,105,106],"Explain why a plain \u003Ccode>setState\u003C\u002Fcode> update never triggers a \u003Ccode>ViewTransition\u003C\u002Fcode> animation, and what has to wrap it instead","Use the four activation props — \u003Ccode>enter\u003C\u002Fcode>, \u003Ccode>exit\u003C\u002Fcode>, \u003Ccode>update\u003C\u002Fcode>, and \u003Ccode>share\u003C\u002Fcode> — and know which DOM change fires which one","Build a shared-element transition (thumbnail → detail) that survives a real re-render, not just a full-page navigation","Tag a transition with \u003Ccode>addTransitionType\u003C\u002Fcode> so the same component animates differently depending on \u003Cem>why\u003C\u002Fem> it changed","Recognize the cases \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> can&#39;t help with — no Transition, no browser support, or content that&#39;s still loading",{"id":108,"html":109,"text":109,"type":94,"level":31},"b7","Who this is for",{"id":111,"html":112,"type":83},"b8","\u003Cp>You&#39;ve written function components with hooks and used \u003Ccode>useTransition\u003C\u002Fcode> or \u003Ccode>startTransition\u003C\u002Fcode> for pending UI at least once. No prior knowledge of the native View Transitions API is assumed, though if you&#39;ve read \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fthe-page-transition-you-pay-a-library-for-is-now-free-2e5d\">the earlier piece on it\u003C\u002Fa>, the mental model here builds directly on top of \u003Ccode>view-transition-name\u003C\u002Fcode> and the \u003Ccode>::view-transition-old\u003C\u002Fcode>\u002F\u003Ccode>::view-transition-new\u003C\u002Fcode> pseudo-elements.\u003C\u002Fp>",{"id":114,"html":115,"text":115,"type":94,"level":31},"b9","Table of contents",{"id":117,"type":100,"items":118,"ordered":18},"b10",[119,120,121,122,123,124,125,126,127,128],"\u003Ca href=\"#the-problem-an-animation-that-only-survives-a-full-page-load\">The problem: an animation that only survives a full page load\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-a-transition-decides-whether-anything-animates-at-all\">The mental model: a Transition decides whether anything animates at all\u003C\u002Fa>","\u003Ca href=\"#stage-1-enter-and-exit--the-smallest-activation\">Stage 1: enter and exit — the smallest activation\u003C\u002Fa>","\u003Ca href=\"#stage-2-update--animating-a-resize-in-place\">Stage 2: update — animating a resize in place\u003C\u002Fa>","\u003Ca href=\"#stage-3-share--a-shared-element-transition-across-a-re-render\">Stage 3: share — a shared-element transition across a re-render\u003C\u002Fa>","\u003Ca href=\"#stage-4-naming-the-transition-with-addtransitiontype\">Stage 4: naming the transition with addTransitionType\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices-when-to-reach-for-viewtransition\">Best practices: when to reach for ViewTransition\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>",{"id":130,"html":131,"text":131,"type":94,"level":31},"b11","The problem: an animation that only survives a full page load",{"id":133,"html":134,"type":83},"b12","\u003Cp>The browser&#39;s native View Transitions API is beautifully simple for exactly one shape of change: a full document swap, or a DOM mutation you make yourself, synchronously, inside a callback:\u003C\u002Fp>",{"id":136,"code":137,"type":138,"language":139,"highlight":140},"b13","\u002F\u002F The \"wrong way first\" — wiring the native API up by hand inside a React app\nfunction selectPhoto(id) {\n  if (!document.startViewTransition) {\n    setSelectedId(id); \u002F\u002F no support: just update, no animation\n    return;\n  }\n  document.startViewTransition(() => {\n    setSelectedId(id); \u002F\u002F ⚠️ setState is async — the DOM hasn't changed yet\n  });\n}","code","js",[],{"id":142,"html":143,"type":83},"b14","\u003Cp>\u003Ccode>startViewTransition()\u003C\u002Fcode> expects its callback to make the DOM change and finish before it takes its &quot;after&quot; snapshot. But \u003Ccode>setSelectedId\u003C\u002Fcode> doesn&#39;t touch the DOM — it schedules a re-render. By the time React actually commits the new list, the browser has already taken its snapshot and started animating between two identical frames. You can make this work by wrapping the call in \u003Ccode>flushSync\u003C\u002Fcode>, but now every click forces a synchronous, blocking render — the exact cost React&#39;s concurrent rendering exists to avoid. And even then, you&#39;ve only solved \u003Cem>this\u003C\u002Fem> click. Add a search filter that also changes which thumbnails exist, and the browser has no idea which old element corresponds to which new one; it just cross-fades the whole container.\u003C\u002Fp>",{"id":145,"html":146,"type":83},"b15","\u003Cp>The actual problem: the native API assumes the code calling it fully controls when the DOM change happens. React doesn&#39;t — a \u003Ccode>setState\u003C\u002Fcode> call describes an \u003Cem>intent\u003C\u002Fem> to update, and the actual commit can be deferred, batched, or (with Suspense) held until data arrives. The stable \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> component exists to close that gap: it hooks into React&#39;s own commit and Suspense machinery instead of assuming a single synchronous mutation.\u003C\u002Fp>",{"id":148,"html":149,"text":149,"type":94,"level":31},"b16","The mental model: a Transition decides whether anything animates at all",{"id":151,"html":152,"type":83},"b17","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> doesn&#39;t animate every DOM change inside it — it only animates changes that happen as part of a \u003Cstrong>Transition\u003C\u002Fstrong>, the same concept \u003Ccode>useTransition\u003C\u002Fcode> and \u003Ccode>startTransition\u003C\u002Fcode> already use to mark an update as non-urgent. Wrap the element you want to animate, then make the state change that affects it through \u003Ccode>startTransition\u003C\u002Fcode> (directly, through a Transition-aware router, or through \u003Ccode>useDeferredValue\u003C\u002Fcode>). A \u003Ccode>ViewTransition\u003C\u002Fcode> sitting around an element that changes via a plain, untransitioned \u003Ccode>setState\u003C\u002Fcode> renders instantly, with no animation, exactly as if the component weren&#39;t there.\u003C\u002Fp>",{"id":154,"code":155,"type":138,"language":156,"highlight":157},"b18","import { ViewTransition, startTransition, useState } from \"react\";\n\nfunction Gallery() {\n  const [selectedId, setSelectedId] = useState(null);\n\n  return (\n    \u003Cbutton\n      onClick={() => {\n        \u002F\u002F The state change is wrapped in a Transition — this is what\n        \u002F\u002F lets any \u003CViewTransition> below react to it at all.\n        startTransition(() => setSelectedId(42));\n      }}\n    >\n      Open photo\n    \u003C\u002Fbutton>\n  );\n}","jsx",[],{"id":159,"html":160,"type":83},"b19","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> &quot;Transition&quot; here is not a metaphor for &quot;animation&quot; — it&#39;s the specific React primitive that marks an update as interruptible and lower-priority. \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> reuses that exact signal to decide \u003Cem>when\u003C\u002Fem> to even ask the browser for a snapshot. No Transition, no snapshot, no animation — by design, not by accident.\u003C\u002Fp>",{"id":162,"html":163,"type":83},"b20","\u003Cp>Once an update is a Transition, \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> classifies what happened to each wrapped element into exactly one of four shapes, and animates accordingly:\u003C\u002Fp>",{"id":165,"head":166,"rows":169,"type":182},"b21",[167,168],"Prop","Fires when…",[170,173,176,179],[171,172],"\u003Ccode>enter\u003C\u002Fcode>","This \u003Ccode>ViewTransition\u003C\u002Fcode> is the first thing inserted, anywhere in the tree, during this Transition",[174,175],"\u003Ccode>exit\u003C\u002Fcode>","This \u003Ccode>ViewTransition\u003C\u002Fcode> is the first thing removed during this Transition",[177,178],"\u003Ccode>update\u003C\u002Fcode>","The element stays, but something inside it changed — content, size, or position, often because a sibling resized",[180,181],"\u003Ccode>share\u003C\u002Fcode>","A named \u003Ccode>ViewTransition\u003C\u002Fcode> in a removed subtree shares its \u003Ccode>name\u003C\u002Fcode> with one in an inserted subtree — React treats them as the same visual element and animates between them","table",{"id":184,"html":185,"text":185,"type":94,"level":31},"b22","Stage 1: enter and exit — the smallest activation",{"id":187,"html":188,"type":83},"b23","\u003Cp>Start with the shape that needs the least setup: something appearing or disappearing.\u003C\u002Fp>",{"id":190,"code":191,"type":138,"language":156,"highlight":192},"b24","import { ViewTransition, startTransition, useState } from \"react\";\n\nfunction Notice({ message }) {\n  return (\n    \u003CViewTransition enter=\"slide-in\" exit=\"fade-out\">\n      \u003Cdiv className=\"notice\">{message}\u003C\u002Fdiv>\n    \u003C\u002FViewTransition>\n  );\n}\n\nfunction App() {\n  const [notice, setNotice] = useState(null);\n  return (\n    \u003C>\n      \u003Cbutton\n        onClick={() => startTransition(() => setNotice(\"Saved!\"))}\n      >\n        Save\n      \u003C\u002Fbutton>\n      {notice && \u003CNotice message={notice} \u002F>}\n    \u003C\u002F>\n  );\n}",[],{"id":194,"html":195,"type":83},"b25","\u003Cp>\u003Ccode>enter\u003C\u002Fcode>\u002F\u003Ccode>exit\u003C\u002Fcode> accept \u003Ccode>&quot;auto&quot;\u003C\u002Fcode> (the browser&#39;s default cross-fade), \u003Ccode>&quot;none&quot;\u003C\u002Fcode>, or a class name whose CSS you write yourself, styling the browser&#39;s own \u003Ccode>::view-transition-new(*)\u003C\u002Fcode>\u002F\u003Ccode>::view-transition-old(*)\u003C\u002Fcode> pseudo-elements — the same pseudo-elements the native API exposes, just targeted through a name React assigns for you instead of one you manage by hand.\u003C\u002Fp>",{"id":197,"code":198,"type":138,"language":199,"highlight":200},"b26","::view-transition-new(.slide-in) {\n  animation: slide-in 200ms ease-out;\n}\n@keyframes slide-in {\n  from { transform: translateY(-12px); opacity: 0; }\n}","css",[],{"id":202,"html":203,"type":83},"b27","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> you are not writing \u003Ccode>document.startViewTransition\u003C\u002Fcode> anywhere. React decides when to call the browser API, based on the Transition boundary; you only decide \u003Cem>how the animation looks\u003C\u002Fem> once it does.\u003C\u002Fp>",{"id":205,"html":206,"text":206,"type":94,"level":31},"b28","Stage 2: update — animating a resize in place",{"id":208,"html":209,"type":83},"b29","\u003Cp>An element that stays mounted but changes shape needs \u003Ccode>update\u003C\u002Fcode>, not \u003Ccode>enter\u003C\u002Fcode>\u002F\u003Ccode>exit\u003C\u002Fcode> — this is also what fires on a sibling whose own resize pushes this element to a new position, even if this element&#39;s own content didn&#39;t change:\u003C\u002Fp>",{"id":211,"code":212,"type":138,"language":156,"highlight":213},"b30","function ExpandableCard({ expanded, onToggle, summary, detail }) {\n  return (\n    \u003CViewTransition update=\"auto\">\n      \u003Cdiv className=\"card\" onClick={onToggle}>\n        \u003Cp>{summary}\u003C\u002Fp>\n        {expanded && \u003Cp className=\"detail\">{detail}\u003C\u002Fp>}\n      \u003C\u002Fdiv>\n    \u003C\u002FViewTransition>\n  );\n}",[],{"id":215,"html":216,"type":83},"b31","\u003Cp>Clicked through a Transition, the card&#39;s height change animates smoothly instead of snapping — and any sibling card pushed down the page animates its own repositioning too, because a \u003Cem>position\u003C\u002Fem> change from a neighbor&#39;s resize is exactly what \u003Ccode>update\u003C\u002Fcode> is defined to cover.\u003C\u002Fp>",{"id":218,"html":219,"text":219,"type":94,"level":31},"b32","Stage 3: share — a shared-element transition across a re-render",{"id":221,"html":222,"type":83},"b33","\u003Cp>This is the shape the opening problem needed, and it&#39;s the one the raw browser API structurally can&#39;t do inside a dynamic list: the same visual element exists in two different subtrees — a thumbnail in a grid, a hero image in a detail view — and you want React to treat them as one continuous element across the transition, even though, as far as the reconciler is concerned, one unmounted and a different one mounted.\u003C\u002Fp>",{"id":224,"code":225,"type":138,"language":156,"highlight":226},"b34","function Thumbnail({ photo, onSelect }) {\n  return (\n    \u003CViewTransition name={`photo-${photo.id}`}>\n      \u003Cimg\n        src={photo.thumbUrl}\n        onClick={() => startTransition(() => onSelect(photo.id))}\n      \u002F>\n    \u003C\u002FViewTransition>\n  );\n}\n\nfunction DetailView({ photo }) {\n  return (\n    \u003CViewTransition name={`photo-${photo.id}`}>\n      \u003Cimg src={photo.fullUrl} className=\"detail-image\" \u002F>\n    \u003C\u002FViewTransition>\n  );\n}",[],{"id":228,"html":229,"type":83},"b35","\u003Cp>Give the outgoing and incoming elements the same \u003Ccode>name\u003C\u002Fcode>, and if one is removed while the other is inserted \u003Cstrong>in the same Transition\u003C\u002Fstrong>, React pairs them as a shared-element transition — the browser morphs position and size from the small thumbnail to the full image, the way you&#39;d hand-wire \u003Ccode>view-transition-name\u003C\u002Fcode> on a static page, except this now survives whatever filtering or sorting also happened in that same click. This is precisely the case from the opening bug: the naive \u003Ccode>flushSync\u003C\u002Fcode> version couldn&#39;t tell the browser &quot;this thumbnail \u003Cem>is\u003C\u002Fem> that hero image&quot; once a filter changed which items existed; naming both sides is what makes the identity explicit instead of positional.\u003C\u002Fp>",{"id":231,"html":232,"type":83},"b36","\u003Cp>This is also why \u003Ccode>share\u003C\u002Fcode> has to be a React-level concept, not a CSS one: whether the outgoing thumbnail and the incoming hero image are &quot;the same element&quot; is exactly the identity question \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-re-render-vs-remount-what-actually-triggers-each-5fok\">React Re-render vs Remount: What Actually Triggers Each\u003C\u002Fa> covers for ordinary reconciliation — \u003Ccode>share\u003C\u002Fcode> just answers it explicitly with a \u003Ccode>name\u003C\u002Fcode>, for the one case (two different subtrees) where React&#39;s own fiber identity can&#39;t do it for you.\u003C\u002Fp>",{"id":234,"html":235,"type":83},"b37","\u003C!-- playground:start -->",{"id":237,"html":238,"text":238,"type":94,"level":31},"b38","🎮 Try it yourself",{"id":240,"html":241,"type":83},"b39","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-viewtransition-component\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":243,"html":244,"type":83},"b40","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":246,"html":247,"type":83},"b41","\u003C!-- playground:end -->",{"id":249,"html":250,"text":250,"type":94,"level":31},"b42","Stage 4: naming the transition with addTransitionType",{"id":252,"html":253,"type":83},"b43","\u003Cp>Every one of the animations above can also be told \u003Cem>why\u003C\u002Fem> it&#39;s happening, so the same \u003Ccode>ViewTransition\u003C\u002Fcode> can look different for &quot;the user navigated forward&quot; versus &quot;the user navigated back&quot; — without threading a prop down to describe it:\u003C\u002Fp>",{"id":255,"code":256,"type":138,"language":156,"highlight":257},"b44","import { addTransitionType, startTransition } from \"react\";\n\nfunction goToNext() {\n  startTransition(() => {\n    addTransitionType(\"nav-forward\");\n    setPage((p) => p + 1);\n  });\n}\n\nfunction goToPrevious() {\n  startTransition(() => {\n    addTransitionType(\"nav-back\");\n    setPage((p) => p - 1);\n  });\n}",[],{"id":259,"html":260,"type":83},"b45","\u003Cp>React forwards whatever string you pass as a browser view-transition \u003Cem>type\u003C\u002Fem>, so your CSS can target it directly — but \u003Ccode>:active-view-transition-type()\u003C\u002Fcode> only ever matches the document root, so it has to wrap the pseudo-element rule rather than chain after it:\u003C\u002Fp>",{"id":262,"code":263,"type":138,"language":199,"highlight":264},"b46",":root:active-view-transition-type(nav-back) {\n  &::view-transition-old(.card) {\n    animation: slide-out-right 200ms;\n  }\n}",[],{"id":266,"html":267,"type":83},"b47","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>addTransitionType\u003C\u002Fcode> doesn&#39;t change \u003Cem>whether\u003C\u002Fem> something animates (that&#39;s still \u003Ccode>enter\u003C\u002Fcode>\u002F\u003Ccode>exit\u003C\u002Fcode>\u002F\u003Ccode>update\u003C\u002Fcode>\u002F\u003Ccode>share\u003C\u002Fcode>, decided per-element) — it only labels the transition so your CSS can branch on cause. One \u003Ccode>ViewTransition\u003C\u002Fcode> handles both directions; the label is what tells them apart.\u003C\u002Fp>",{"id":269,"html":270,"text":270,"type":94,"level":31},"b48","Edge cases and gotchas",{"id":272,"type":100,"items":273,"ordered":18},"b49",[274,275,276,277,278,279],"\u003Cstrong>A \u003Ccode>ViewTransition\u003C\u002Fcode> around an element that changes outside any Transition is inert.\u003C\u002Fstrong> If you find an animation just isn&#39;t firing, check the state update first — a plain \u003Ccode>onClick={() =&gt; setState(x)}\u003C\u002Fcode> is the single most common reason, not a missing prop.","\u003Cstrong>\u003Ccode>share\u003C\u002Fcode> needs the name to be unique in each subtree at the moment of the swap\u003C\u002Fstrong>, exactly like the native API&#39;s \u003Ccode>view-transition-name\u003C\u002Fcode>. Two simultaneously-mounted elements with the same \u003Ccode>name\u003C\u002Fcode> isn&#39;t a silent quirk — React detects it and errors, logging both offending elements in development — so a dynamic name derived from a stable ID (\u003Ccode>photo-${id}\u003C\u002Fcode>), not the array index, is what makes this safe in a re-orderable list.","\u003Cstrong>Suspense-blocked content usually delays the &quot;after&quot; snapshot, not the animation itself\u003C\u002Fstrong> — React waits for newly-suspended content to be ready before asking the browser to animate, so a slow detail view doesn&#39;t produce a transition into a loading spinner; the whole Transition just starts later. The one case that&#39;s stronger than a delay: if a Suspense fallback actually appears \u003Cem>between\u003C\u002Fem> a \u003Ccode>share\u003C\u002Fcode> pair&#39;s removal and its matching insertion, the shared-element transition doesn&#39;t happen at all for that pair — worth knowing before you assume every Suspense-gated update animates, just late.","\u003Cstrong>Multiple Transitions in flight no longer block each other.\u003C\u002Fstrong> As of the same 19.3 release, transitions render independently rather than being entangled into a single render, so triggering a second, unrelated Transition while a slow one is still resolving doesn&#39;t stall it.","\u003Cstrong>Browser support isn&#39;t universal.\u003C\u002Fstrong> The View Transitions API this all sits on top of is a Chromium-and-Safari-shipped feature with gaps elsewhere; without support, \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> should degrade to an instant, unanimated update rather than an error — verify that in your own target browsers rather than assuming it, and never let a missing API break the update itself.","\u003Cstrong>This is not a general-purpose animation library.\u003C\u002Fstrong> There&#39;s no spring physics, no stagger helper, no timeline — you&#39;re styling two browser pseudo-elements with CSS. For anything beyond enter\u002Fexit\u002Fresize\u002Fshared-element, a dedicated animation library is still the right tool.",{"id":281,"html":282,"text":282,"type":94,"level":31},"b50","Best practices: when to reach for ViewTransition",{"id":284,"type":100,"items":285,"ordered":18},"b51",[286,287,288,289],"\u003Cstrong>Reach for it\u003C\u002Fstrong> when an interaction already goes through a Transition (a route change, a filter, a tab switch, an optimistic update) and the \u003Cem>result\u003C\u002Fem> deserves to feel continuous — a list reordering, a card expanding, a thumbnail opening into detail.","\u003Cstrong>Name only what needs to persist visually.\u003C\u002Fstrong> Give \u003Ccode>name\u003C\u002Fcode> to the handful of elements that are the actual subject of a shared-element transition; wrapping everything in a named \u003Ccode>ViewTransition\u003C\u002Fcode> just adds bookkeeping for animations nobody will notice.","\u003Cstrong>Don&#39;t force an update into a Transition just to get an animation.\u003C\u002Fstrong> If the update isn&#39;t naturally low-priority or interruptible, wrapping it in \u003Ccode>startTransition\u003C\u002Fcode> to unlock \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> is solving the wrong problem — a plain CSS transition on the element may be simpler and more honest about what&#39;s actually happening.","\u003Cstrong>Avoid it when\u003C\u002Fstrong> the change already crosses a full page navigation between separately loaded documents — that&#39;s still the native \u003Ccode>@view-transition { navigation: auto; }\u003C\u002Fcode> CSS rule&#39;s job, not this component&#39;s.",{"id":291,"html":292,"text":292,"type":94,"level":31},"b52","FAQ",{"id":294,"html":295,"text":296,"type":94,"level":43},"b53","Why doesn&#39;t wrapping an element in ViewTransition make it animate?","Why doesn't wrapping an element in ViewTransition make it animate?",{"id":298,"html":299,"type":83},"b54","\u003Cp>Because activation depends on the state change happening inside a Transition (\u003Ccode>startTransition\u003C\u002Fcode>, \u003Ccode>useTransition\u003C\u002Fcode>, or a Transition-aware router navigation), not on the presence of the \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> wrapper alone. A plain \u003Ccode>setState\u003C\u002Fcode> renders instantly and skips the browser&#39;s view-transition machinery entirely, by design.\u003C\u002Fp>",{"id":301,"html":302,"text":302,"type":94,"level":43},"b55","Do I need the native View Transitions API to use this component?",{"id":304,"html":305,"type":83},"b56","\u003Cp>You don&#39;t call \u003Ccode>document.startViewTransition\u003C\u002Fcode> yourself — React does that internally when it detects the underlying browser supports it. You do still rely on the same browser feature under the hood, so support follows the native API&#39;s own availability.\u003C\u002Fp>",{"id":307,"html":308,"text":309,"type":94,"level":43},"b57","What&#39;s the difference between update and share?","What's the difference between update and share?",{"id":311,"html":312,"type":83},"b58","\u003Cp>\u003Ccode>update\u003C\u002Fcode> is for an element that stays the same element across the Transition but changes in place (content, size, position from a sibling&#39;s resize). \u003Ccode>share\u003C\u002Fcode> is for two \u003Cem>different\u003C\u002Fem> elements — one removed, one inserted — that you&#39;re telling React to treat as a single continuous element by giving them matching \u003Ccode>name\u003C\u002Fcode>s.\u003C\u002Fp>",{"id":314,"html":315,"text":315,"type":94,"level":43},"b59","Can I use ViewTransition with Suspense-loaded data?",{"id":317,"html":318,"type":83},"b60","\u003Cp>Yes — that&#39;s a core reason it needed to be a React component rather than a manual API call. It coordinates with Suspense boundaries inside the same Transition, waiting for suspended content to resolve before the animation&#39;s &quot;after&quot; state is captured, instead of animating into a loading fallback.\u003C\u002Fp>",{"id":320,"html":321,"text":321,"type":94,"level":43},"b61","Does the React Compiler affect any of this?",{"id":323,"html":324,"type":83},"b62","\u003Cp>No. The Compiler (stable at 1.0) automates memoization of values computed during render; it has no relationship to Transitions, Suspense, or the browser&#39;s view-transition lifecycle. Nothing here needs the Compiler, and nothing about the Compiler changes how \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> behaves.\u003C\u002Fp>",{"id":326,"html":327,"type":83},"b63","\u003C!-- quiz:start -->",{"id":329,"html":330,"text":330,"type":94,"level":31},"b64","🧠 Test yourself",{"id":332,"html":333,"type":83},"b65","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-viewtransition-component\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":335,"html":336,"type":83},"b66","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":338,"html":339,"type":83},"b67","\u003C!-- quiz:end -->",{"id":341,"html":342,"text":342,"type":94,"level":31},"b68","Cheat sheet",{"id":344,"code":345,"type":138,"language":156,"highlight":346},"b69","import {\n  ViewTransition,\n  startTransition,\n  addTransitionType,\n} from \"react\";\n\n\u002F\u002F 1. The state change MUST go through a Transition, or nothing animates.\nstartTransition(() => setSelectedId(id));\n\n\u002F\u002F 2. Pick the shape:\n\u003CViewTransition enter=\"auto\" exit=\"auto\">      {\u002F* mounts \u002F unmounts *\u002F}\n\u003CViewTransition update=\"auto\">                 {\u002F* stays, resizes\u002Fmoves *\u002F}\n\u003CViewTransition name={`item-${id}`}>           {\u002F* shared element pair  *\u002F}\n\n\u002F\u002F 3. Style the browser pseudo-elements React targets for you:\n\u002F\u002F ::view-transition-old(\u003Cclass-or-name>)\n\u002F\u002F ::view-transition-new(\u003Cclass-or-name>)\n\n\u002F\u002F 4. Label WHY, to branch the CSS on cause:\nstartTransition(() => {\n  addTransitionType(\"nav-back\");\n  setPage(p => p - 1);\n});\n\u002F\u002F :root:active-view-transition-type(nav-back) { &::view-transition-old(.card) { … } }",[],{"id":348,"head":349,"rows":352,"type":182},"b70",[167,350,351],"Use for","Fires on",[353,356,359,362],[171,354,355],"New content appearing","First mount inside this Transition",[174,357,358],"Content disappearing","First unmount inside this Transition",[177,360,361],"Same element, different size\u002Fcontent\u002Fposition","In-place change or a sibling&#39;s resize",[363,364,365],"\u003Ccode>share\u003C\u002Fcode> (via matching \u003Ccode>name\u003C\u002Fcode>)","Thumbnail → detail, list item → expanded card","Matched removal + insertion, same Transition",{"id":367,"html":368,"text":368,"type":94,"level":31},"b71","Key takeaways",{"id":370,"type":100,"items":371,"ordered":18},"b72",[372,373,374,375,376],"\u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> only reacts to changes made inside a React Transition — a plain \u003Ccode>setState\u003C\u002Fcode> is invisible to it, on purpose.","Every activation is one of four shapes: \u003Ccode>enter\u003C\u002Fcode>, \u003Ccode>exit\u003C\u002Fcode>, \u003Ccode>update\u003C\u002Fcode>, or \u003Ccode>share\u003C\u002Fcode> — matching \u003Ccode>name\u003C\u002Fcode>s across a removal and an insertion.","Suspense-blocked content delays when the animation&#39;s &quot;after&quot; state is captured; it doesn&#39;t corrupt the animation.","\u003Ccode>addTransitionType\u003C\u002Fcode> labels \u003Cem>why\u003C\u002Fem> an update happened so one component&#39;s CSS can branch on cause, separate from \u003Cem>whether\u003C\u002Fem> it animates.","It replaces the manual \u003Ccode>document.startViewTransition\u003C\u002Fcode> + \u003Ccode>flushSync\u003C\u002Fcode> workaround, not the browser API itself — you&#39;re still styling \u003Ccode>::view-transition-old\u003C\u002Fcode>\u002F\u003Ccode>::view-transition-new\u003C\u002Fcode>.",{"id":378,"html":379,"text":379,"type":94,"level":31},"b73","Back to that photo grid",{"id":381,"html":382,"type":83},"b74","\u003Cp>The bug in the opening scene wasn&#39;t the animation idea — it was asking a synchronous browser API to describe an asynchronous React update. \u003Ccode>startTransition\u003C\u002Fcode> is what tells \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode> a change is happening at all, and \u003Ccode>name\u003C\u002Fcode> is what tells it which old element the new one is actually replacing, even after a filter reshuffled the whole list around it. Once both are in place, the thumbnail-to-detail morph survives exactly the kind of re-render that broke the hand-wired version — no \u003Ccode>flushSync\u003C\u002Fcode>, no manual snapshot timing.\u003C\u002Fp>",{"id":384,"html":385,"type":83},"b75","\u003Cp>If you&#39;ve been holding off on this kind of animation because the native API only seemed to work on static pages, that&#39;s the gap this component closes. What&#39;s the first shared-element transition you&#39;re going to try it on?\u003C\u002Fp>",{"id":387,"html":388,"type":83},"b76","\u003C!-- related:start -->",{"id":390,"html":391,"text":391,"type":94,"level":31},"b77","📚 Read next",{"id":393,"type":100,"items":394,"ordered":18},"b78",[395,396,397],"\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-derived-state-bug\">React Derived State: Why That useState Is Probably a Bug\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-rerender-vs-remount\">React Re-render vs Remount: What Actually Triggers Each\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-compiler-memoization\">React Compiler 1.0: What useMemo You Can Delete\u003C\u002Fa>",{"id":399,"html":400,"type":83},"b79","\u003C!-- related:end -->",{"id":402,"type":403},"b80","divider",{"id":405,"html":406,"type":83},"b81","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":408,"html":409,"type":83},"b82","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":411,"type":100,"items":412,"ordered":18},"b83",[413,414,415],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","You wire up a photo grid. Click a thumbnail, it should morph smoothly into a full detail view — the kind of transition Google Photos and Instagram have trained everyone to expect. You already know the browser can do this for free: the native View Transitions API wraps a DOM update in `document.startViewTransition()`, tags the old and new elements with a shared `view-transition-name`, and the browser tweens between them. You wire it up. It works beautifully — right up until the thumbnail lives inside a filtered, paginated React list, and the click also has to update `selectedId` state, close a search box, and possibly wait on data that hasn't arrived yet. Now the animation glitches, fires on the wrong element, or doesn't fire at all, and nothing in the DevTools explains why.\n\nThis is episode five of **React Deep Dive**, on what React itself decides rather than JavaScript with a React import. This one is about a decision the reconciler now makes explicitly: which DOM changes are worth animating, and which four shapes every one of those changes takes.\n\nThis article is written against **React 19.3** (verified against React's own `CHANGELOG.md` on GitHub, version 19.3.0, published September 9, 2026), where the `\u003CViewTransition>` component and `addTransitionType` shipped as **stable**, not experimental. Everything here assumes React 19-era function components and hooks.\n\n## What you'll learn\n\nBy the end of this article you'll be able to:\n\n- Explain why a plain `setState` update never triggers a `ViewTransition` animation, and what has to wrap it instead\n- Use the four activation props — `enter`, `exit`, `update`, and `share` — and know which DOM change fires which one\n- Build a shared-element transition (thumbnail → detail) that survives a real re-render, not just a full-page navigation\n- Tag a transition with `addTransitionType` so the same component animates differently depending on *why* it changed\n- Recognize the cases `\u003CViewTransition>` can't help with — no Transition, no browser support, or content that's still loading\n\n## Who this is for\n\nYou've written function components with hooks and used `useTransition` or `startTransition` for pending UI at least once. No prior knowledge of the native View Transitions API is assumed, though if you've read \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fthe-page-transition-you-pay-a-library-for-is-now-free-2e5d\">the earlier piece on it\u003C\u002Fa>, the mental model here builds directly on top of `view-transition-name` and the `::view-transition-old`\u002F`::view-transition-new` pseudo-elements.\n\n## Table of contents\n\n- [The problem: an animation that only survives a full page load](#the-problem-an-animation-that-only-survives-a-full-page-load)\n- [The mental model: a Transition decides whether anything animates at all](#the-mental-model-a-transition-decides-whether-anything-animates-at-all)\n- [Stage 1: enter and exit — the smallest activation](#stage-1-enter-and-exit--the-smallest-activation)\n- [Stage 2: update — animating a resize in place](#stage-2-update--animating-a-resize-in-place)\n- [Stage 3: share — a shared-element transition across a re-render](#stage-3-share--a-shared-element-transition-across-a-re-render)\n- [Stage 4: naming the transition with addTransitionType](#stage-4-naming-the-transition-with-addtransitiontype)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices: when to reach for ViewTransition](#best-practices-when-to-reach-for-viewtransition)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n\n## The problem: an animation that only survives a full page load\n\nThe browser's native View Transitions API is beautifully simple for exactly one shape of change: a full document swap, or a DOM mutation you make yourself, synchronously, inside a callback:\n\n```js\n\u002F\u002F The \"wrong way first\" — wiring the native API up by hand inside a React app\nfunction selectPhoto(id) {\n  if (!document.startViewTransition) {\n    setSelectedId(id); \u002F\u002F no support: just update, no animation\n    return;\n  }\n  document.startViewTransition(() => {\n    setSelectedId(id); \u002F\u002F ⚠️ setState is async — the DOM hasn't changed yet\n  });\n}\n```\n\n`startViewTransition()` expects its callback to make the DOM change and finish before it takes its \"after\" snapshot. But `setSelectedId` doesn't touch the DOM — it schedules a re-render. By the time React actually commits the new list, the browser has already taken its snapshot and started animating between two identical frames. You can make this work by wrapping the call in `flushSync`, but now every click forces a synchronous, blocking render — the exact cost React's concurrent rendering exists to avoid. And even then, you've only solved *this* click. Add a search filter that also changes which thumbnails exist, and the browser has no idea which old element corresponds to which new one; it just cross-fades the whole container.\n\nThe actual problem: the native API assumes the code calling it fully controls when the DOM change happens. React doesn't — a `setState` call describes an *intent* to update, and the actual commit can be deferred, batched, or (with Suspense) held until data arrives. The stable `\u003CViewTransition>` component exists to close that gap: it hooks into React's own commit and Suspense machinery instead of assuming a single synchronous mutation.\n\n## The mental model: a Transition decides whether anything animates at all\n\n**The mental model:** `\u003CViewTransition>` doesn't animate every DOM change inside it — it only animates changes that happen as part of a **Transition**, the same concept `useTransition` and `startTransition` already use to mark an update as non-urgent. Wrap the element you want to animate, then make the state change that affects it through `startTransition` (directly, through a Transition-aware router, or through `useDeferredValue`). A `ViewTransition` sitting around an element that changes via a plain, untransitioned `setState` renders instantly, with no animation, exactly as if the component weren't there.\n\n```jsx\nimport { ViewTransition, startTransition, useState } from \"react\";\n\nfunction Gallery() {\n  const [selectedId, setSelectedId] = useState(null);\n\n  return (\n    \u003Cbutton\n      onClick={() => {\n        \u002F\u002F The state change is wrapped in a Transition — this is what\n        \u002F\u002F lets any \u003CViewTransition> below react to it at all.\n        startTransition(() => setSelectedId(42));\n      }}\n    >\n      Open photo\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Key concept:** \"Transition\" here is not a metaphor for \"animation\" — it's the specific React primitive that marks an update as interruptible and lower-priority. `\u003CViewTransition>` reuses that exact signal to decide *when* to even ask the browser for a snapshot. No Transition, no snapshot, no animation — by design, not by accident.\n\nOnce an update is a Transition, `\u003CViewTransition>` classifies what happened to each wrapped element into exactly one of four shapes, and animates accordingly:\n\n| Prop | Fires when… |\n| --- | --- |\n| `enter` | This `ViewTransition` is the first thing inserted, anywhere in the tree, during this Transition |\n| `exit` | This `ViewTransition` is the first thing removed during this Transition |\n| `update` | The element stays, but something inside it changed — content, size, or position, often because a sibling resized |\n| `share` | A named `ViewTransition` in a removed subtree shares its `name` with one in an inserted subtree — React treats them as the same visual element and animates between them |\n\n## Stage 1: enter and exit — the smallest activation\n\nStart with the shape that needs the least setup: something appearing or disappearing.\n\n```jsx\nimport { ViewTransition, startTransition, useState } from \"react\";\n\nfunction Notice({ message }) {\n  return (\n    \u003CViewTransition enter=\"slide-in\" exit=\"fade-out\">\n      \u003Cdiv className=\"notice\">{message}\u003C\u002Fdiv>\n    \u003C\u002FViewTransition>\n  );\n}\n\nfunction App() {\n  const [notice, setNotice] = useState(null);\n  return (\n    \u003C>\n      \u003Cbutton\n        onClick={() => startTransition(() => setNotice(\"Saved!\"))}\n      >\n        Save\n      \u003C\u002Fbutton>\n      {notice && \u003CNotice message={notice} \u002F>}\n    \u003C\u002F>\n  );\n}\n```\n\n`enter`\u002F`exit` accept `\"auto\"` (the browser's default cross-fade), `\"none\"`, or a class name whose CSS you write yourself, styling the browser's own `::view-transition-new(*)`\u002F`::view-transition-old(*)` pseudo-elements — the same pseudo-elements the native API exposes, just targeted through a name React assigns for you instead of one you manage by hand.\n\n```css\n::view-transition-new(.slide-in) {\n  animation: slide-in 200ms ease-out;\n}\n@keyframes slide-in {\n  from { transform: translateY(-12px); opacity: 0; }\n}\n```\n\n**Key concept:** you are not writing `document.startViewTransition` anywhere. React decides when to call the browser API, based on the Transition boundary; you only decide *how the animation looks* once it does.\n\n## Stage 2: update — animating a resize in place\n\nAn element that stays mounted but changes shape needs `update`, not `enter`\u002F`exit` — this is also what fires on a sibling whose own resize pushes this element to a new position, even if this element's own content didn't change:\n\n```jsx\nfunction ExpandableCard({ expanded, onToggle, summary, detail }) {\n  return (\n    \u003CViewTransition update=\"auto\">\n      \u003Cdiv className=\"card\" onClick={onToggle}>\n        \u003Cp>{summary}\u003C\u002Fp>\n        {expanded && \u003Cp className=\"detail\">{detail}\u003C\u002Fp>}\n      \u003C\u002Fdiv>\n    \u003C\u002FViewTransition>\n  );\n}\n```\n\nClicked through a Transition, the card's height change animates smoothly instead of snapping — and any sibling card pushed down the page animates its own repositioning too, because a *position* change from a neighbor's resize is exactly what `update` is defined to cover.\n\n## Stage 3: share — a shared-element transition across a re-render\n\nThis is the shape the opening problem needed, and it's the one the raw browser API structurally can't do inside a dynamic list: the same visual element exists in two different subtrees — a thumbnail in a grid, a hero image in a detail view — and you want React to treat them as one continuous element across the transition, even though, as far as the reconciler is concerned, one unmounted and a different one mounted.\n\n```jsx\nfunction Thumbnail({ photo, onSelect }) {\n  return (\n    \u003CViewTransition name={`photo-${photo.id}`}>\n      \u003Cimg\n        src={photo.thumbUrl}\n        onClick={() => startTransition(() => onSelect(photo.id))}\n      \u002F>\n    \u003C\u002FViewTransition>\n  );\n}\n\nfunction DetailView({ photo }) {\n  return (\n    \u003CViewTransition name={`photo-${photo.id}`}>\n      \u003Cimg src={photo.fullUrl} className=\"detail-image\" \u002F>\n    \u003C\u002FViewTransition>\n  );\n}\n```\n\nGive the outgoing and incoming elements the same `name`, and if one is removed while the other is inserted **in the same Transition**, React pairs them as a shared-element transition — the browser morphs position and size from the small thumbnail to the full image, the way you'd hand-wire `view-transition-name` on a static page, except this now survives whatever filtering or sorting also happened in that same click. This is precisely the case from the opening bug: the naive `flushSync` version couldn't tell the browser \"this thumbnail *is* that hero image\" once a filter changed which items existed; naming both sides is what makes the identity explicit instead of positional.\n\nThis is also why `share` has to be a React-level concept, not a CSS one: whether the outgoing thumbnail and the incoming hero image are \"the same element\" is exactly the identity question \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-re-render-vs-remount-what-actually-triggers-each-5fok\">React Re-render vs Remount: What Actually Triggers Each\u003C\u002Fa> covers for ordinary reconciliation — `share` just answers it explicitly with a `name`, for the one case (two different subtrees) where React's own fiber identity can't do it for you.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-viewtransition-component\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## Stage 4: naming the transition with addTransitionType\n\nEvery one of the animations above can also be told *why* it's happening, so the same `ViewTransition` can look different for \"the user navigated forward\" versus \"the user navigated back\" — without threading a prop down to describe it:\n\n```jsx\nimport { addTransitionType, startTransition } from \"react\";\n\nfunction goToNext() {\n  startTransition(() => {\n    addTransitionType(\"nav-forward\");\n    setPage((p) => p + 1);\n  });\n}\n\nfunction goToPrevious() {\n  startTransition(() => {\n    addTransitionType(\"nav-back\");\n    setPage((p) => p - 1);\n  });\n}\n```\n\nReact forwards whatever string you pass as a browser view-transition *type*, so your CSS can target it directly — but `:active-view-transition-type()` only ever matches the document root, so it has to wrap the pseudo-element rule rather than chain after it:\n\n```css\n:root:active-view-transition-type(nav-back) {\n  &::view-transition-old(.card) {\n    animation: slide-out-right 200ms;\n  }\n}\n```\n\n**Key concept:** `addTransitionType` doesn't change *whether* something animates (that's still `enter`\u002F`exit`\u002F`update`\u002F`share`, decided per-element) — it only labels the transition so your CSS can branch on cause. One `ViewTransition` handles both directions; the label is what tells them apart.\n\n## Edge cases and gotchas\n\n- **A `ViewTransition` around an element that changes outside any Transition is inert.** If you find an animation just isn't firing, check the state update first — a plain `onClick={() => setState(x)}` is the single most common reason, not a missing prop.\n- **`share` needs the name to be unique in each subtree at the moment of the swap**, exactly like the native API's `view-transition-name`. Two simultaneously-mounted elements with the same `name` isn't a silent quirk — React detects it and errors, logging both offending elements in development — so a dynamic name derived from a stable ID (`photo-${id}`), not the array index, is what makes this safe in a re-orderable list.\n- **Suspense-blocked content usually delays the \"after\" snapshot, not the animation itself** — React waits for newly-suspended content to be ready before asking the browser to animate, so a slow detail view doesn't produce a transition into a loading spinner; the whole Transition just starts later. The one case that's stronger than a delay: if a Suspense fallback actually appears *between* a `share` pair's removal and its matching insertion, the shared-element transition doesn't happen at all for that pair — worth knowing before you assume every Suspense-gated update animates, just late.\n- **Multiple Transitions in flight no longer block each other.** As of the same 19.3 release, transitions render independently rather than being entangled into a single render, so triggering a second, unrelated Transition while a slow one is still resolving doesn't stall it.\n- **Browser support isn't universal.** The View Transitions API this all sits on top of is a Chromium-and-Safari-shipped feature with gaps elsewhere; without support, `\u003CViewTransition>` should degrade to an instant, unanimated update rather than an error — verify that in your own target browsers rather than assuming it, and never let a missing API break the update itself.\n- **This is not a general-purpose animation library.** There's no spring physics, no stagger helper, no timeline — you're styling two browser pseudo-elements with CSS. For anything beyond enter\u002Fexit\u002Fresize\u002Fshared-element, a dedicated animation library is still the right tool.\n\n## Best practices: when to reach for ViewTransition\n\n- **Reach for it** when an interaction already goes through a Transition (a route change, a filter, a tab switch, an optimistic update) and the *result* deserves to feel continuous — a list reordering, a card expanding, a thumbnail opening into detail.\n- **Name only what needs to persist visually.** Give `name` to the handful of elements that are the actual subject of a shared-element transition; wrapping everything in a named `ViewTransition` just adds bookkeeping for animations nobody will notice.\n- **Don't force an update into a Transition just to get an animation.** If the update isn't naturally low-priority or interruptible, wrapping it in `startTransition` to unlock `\u003CViewTransition>` is solving the wrong problem — a plain CSS transition on the element may be simpler and more honest about what's actually happening.\n- **Avoid it when** the change already crosses a full page navigation between separately loaded documents — that's still the native `@view-transition { navigation: auto; }` CSS rule's job, not this component's.\n\n## FAQ\n\n### Why doesn't wrapping an element in ViewTransition make it animate?\n\nBecause activation depends on the state change happening inside a Transition (`startTransition`, `useTransition`, or a Transition-aware router navigation), not on the presence of the `\u003CViewTransition>` wrapper alone. A plain `setState` renders instantly and skips the browser's view-transition machinery entirely, by design.\n\n### Do I need the native View Transitions API to use this component?\n\nYou don't call `document.startViewTransition` yourself — React does that internally when it detects the underlying browser supports it. You do still rely on the same browser feature under the hood, so support follows the native API's own availability.\n\n### What's the difference between update and share?\n\n`update` is for an element that stays the same element across the Transition but changes in place (content, size, position from a sibling's resize). `share` is for two *different* elements — one removed, one inserted — that you're telling React to treat as a single continuous element by giving them matching `name`s.\n\n### Can I use ViewTransition with Suspense-loaded data?\n\nYes — that's a core reason it needed to be a React component rather than a manual API call. It coordinates with Suspense boundaries inside the same Transition, waiting for suspended content to resolve before the animation's \"after\" state is captured, instead of animating into a loading fallback.\n\n### Does the React Compiler affect any of this?\n\nNo. The Compiler (stable at 1.0) automates memoization of values computed during render; it has no relationship to Transitions, Suspense, or the browser's view-transition lifecycle. Nothing here needs the Compiler, and nothing about the Compiler changes how `\u003CViewTransition>` behaves.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-viewtransition-component\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## Cheat sheet\n\n```jsx\nimport {\n  ViewTransition,\n  startTransition,\n  addTransitionType,\n} from \"react\";\n\n\u002F\u002F 1. The state change MUST go through a Transition, or nothing animates.\nstartTransition(() => setSelectedId(id));\n\n\u002F\u002F 2. Pick the shape:\n\u003CViewTransition enter=\"auto\" exit=\"auto\">      {\u002F* mounts \u002F unmounts *\u002F}\n\u003CViewTransition update=\"auto\">                 {\u002F* stays, resizes\u002Fmoves *\u002F}\n\u003CViewTransition name={`item-${id}`}>           {\u002F* shared element pair  *\u002F}\n\n\u002F\u002F 3. Style the browser pseudo-elements React targets for you:\n\u002F\u002F ::view-transition-old(\u003Cclass-or-name>)\n\u002F\u002F ::view-transition-new(\u003Cclass-or-name>)\n\n\u002F\u002F 4. Label WHY, to branch the CSS on cause:\nstartTransition(() => {\n  addTransitionType(\"nav-back\");\n  setPage(p => p - 1);\n});\n\u002F\u002F :root:active-view-transition-type(nav-back) { &::view-transition-old(.card) { … } }\n```\n\n| Prop | Use for | Fires on |\n| --- | --- | --- |\n| `enter` | New content appearing | First mount inside this Transition |\n| `exit` | Content disappearing | First unmount inside this Transition |\n| `update` | Same element, different size\u002Fcontent\u002Fposition | In-place change or a sibling's resize |\n| `share` (via matching `name`) | Thumbnail → detail, list item → expanded card | Matched removal + insertion, same Transition |\n\n## Key takeaways\n\n- `\u003CViewTransition>` only reacts to changes made inside a React Transition — a plain `setState` is invisible to it, on purpose.\n- Every activation is one of four shapes: `enter`, `exit`, `update`, or `share` — matching `name`s across a removal and an insertion.\n- Suspense-blocked content delays when the animation's \"after\" state is captured; it doesn't corrupt the animation.\n- `addTransitionType` labels *why* an update happened so one component's CSS can branch on cause, separate from *whether* it animates.\n- It replaces the manual `document.startViewTransition` + `flushSync` workaround, not the browser API itself — you're still styling `::view-transition-old`\u002F`::view-transition-new`.\n\n## Back to that photo grid\n\nThe bug in the opening scene wasn't the animation idea — it was asking a synchronous browser API to describe an asynchronous React update. `startTransition` is what tells `\u003CViewTransition>` a change is happening at all, and `name` is what tells it which old element the new one is actually replacing, even after a filter reshuffled the whole list around it. Once both are in place, the thumbnail-to-detail morph survives exactly the kind of re-render that broke the hand-wired version — no `flushSync`, no manual snapshot timing.\n\nIf you've been holding off on this kind of animation because the native API only seemed to work on static pages, that's the gap this component closes. What's the first shared-element transition you're going to try it on?\n\n\u003C!-- related:start -->\n\n## 📚 Read next\n\n- [React Derived State: Why That useState Is Probably a Bug](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-derived-state-bug)\n- [React Re-render vs Remount: What Actually Triggers Each](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-rerender-vs-remount)\n- [React Compiler 1.0: What useMemo You Can Delete](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-compiler-memoization)\n\n\u003C!-- related:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":418,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-viewtransition-component","01a0f615-d2be-750b-839e-04dbca9965e3",{"name":421,"part":422,"total":422,"items":423},"React Deep Dive",5,[424,429,433,437,442],{"slug":425,"title":426,"publishedAt":427,"readingMinutes":428},"react-weekly-rerender-vs-remount","React Re-render vs Remount: What Actually Triggers Each","2026-08-29T12:32:41.200Z",13,{"slug":430,"title":431,"publishedAt":432,"readingMinutes":428},"react-weekly-compiler-memoization","React Compiler 1.0: What useMemo You Can Delete","2026-09-05T10:17:49.849Z",{"slug":434,"title":435,"publishedAt":436,"readingMinutes":428},"react-weekly-form-actions-pending-state","React Form Actions: useActionState & useFormStatus Guide","2026-09-12T10:23:52.928Z",{"slug":438,"title":439,"publishedAt":440,"readingMinutes":441},"react-weekly-derived-state-bug","React Derived State: Why That useState Is Probably a Bug","2026-09-20T16:27:07.271Z",14,{"slug":46,"title":47,"publishedAt":52,"readingMinutes":51},{"id":444,"locked":18},"01a0f615-d2e7-72ce-b6bc-2bc802b5b1f6",[446],{"id":447,"slug":46,"title":448,"_count":449},"01a0f615-d303-7697-95e0-5d515421cce6","React ViewTransition",{"questions":450},8,[452],{"locale":13,"slug":46},{"id":447,"slug":46,"title":448,"_count":454,"questionCount":450},{"questions":450},[456,458,460,464,466,468,472,473,476,480,483,486],{"slug":72,"name":73,"articles":457},121,{"slug":66,"name":67,"articles":459},100,{"slug":461,"name":462,"articles":463},"frontend","Frontend",78,{"slug":69,"name":70,"articles":465},46,{"slug":199,"name":467,"articles":53},"Css",{"slug":469,"name":470,"articles":471},"typescript","Typescript",18,{"slug":61,"name":62,"articles":51},{"slug":474,"name":475,"articles":51},"performance","Performance",{"slug":477,"name":478,"articles":479},"browser","Browser",11,{"slug":481,"name":482,"articles":479},"node","Node",{"slug":484,"name":485,"articles":450},"html","Html",{"slug":487,"name":488,"articles":489},"accessibility","Accessibility",7,{"items":491,"meta":605},[492,504,522,537,557,572,590],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":493,"likeCount":19,"commentCount":19,"author":494,"vertical":495,"topic":496,"tags":497,"_count":502,"playground":503,"hasQuiz":17,"hasPlayground":17},40,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[498,499,500,501],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":46},{"id":505,"slug":506,"title":507,"subtitle":48,"excerpt":508,"coverUrl":509,"locale":13,"readingMinutes":422,"publishedAt":510,"viewCount":511,"likeCount":19,"commentCount":19,"author":512,"vertical":513,"topic":514,"tags":515,"_count":520,"playground":521,"hasQuiz":17,"hasPlayground":17},"01a0ec73-247c-7738-84ae-80c9d1d116a8","input-lag-bug-debounce-cant-fix","Your Search Box Lags. Debounce Hides It; `useTransition` Fixes It","A big list re-render was blocking every keystroke — debouncing just moved the freeze. useTransition tells React which update can wait.","\u002Fmedia\u002Fcovers\u002Finput-lag-bug-debounce-cant-fix.png","2026-10-03T11:21:55.564Z",35,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[516,517,518,519],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":72,"name":73,"color":48},{"slug":474,"name":475,"color":48},{"assessments":75},{"slug":506},{"id":523,"slug":438,"title":439,"subtitle":48,"excerpt":524,"coverUrl":525,"locale":13,"readingMinutes":441,"publishedAt":440,"viewCount":526,"likeCount":19,"commentCount":19,"author":527,"vertical":528,"topic":529,"tags":530,"_count":535,"playground":536,"hasQuiz":17,"hasPlayground":17},"01a0ab57-dda2-74dd-816b-a0ef7f77aeb3","React derived state explained: why syncing a computed value into useState with an Effect causes extra renders and drift, and what to write instead.","\u002Fmedia\u002Fcovers\u002Freact-weekly-derived-state-bug.png",345,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[531,532,533,534],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":438},{"id":538,"slug":539,"title":540,"subtitle":48,"excerpt":541,"coverUrl":542,"locale":13,"readingMinutes":441,"publishedAt":543,"viewCount":544,"likeCount":19,"commentCount":19,"author":545,"vertical":546,"topic":547,"tags":550,"_count":555,"playground":556,"hasQuiz":17,"hasPlayground":17},"01a09181-2f3b-7349-b651-fd9cdda0b931","nextjs-weekly-parallel-intercepting-routes-modals","Next.js Parallel & Intercepting Routes: Modals Done Right","How Next.js parallel routes (@slot) and intercepting routes ((.), (..), (...)) combine to build shareable, refreshable modals — verified against Next.js 16.3.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-parallel-intercepting-routes-modals.png","2026-09-15T17:50:47.218Z",360,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":548,"name":549},"nextjs","Nextjs",[551,552,553,554],{"slug":548,"name":549,"color":48},{"slug":61,"name":62,"color":48},{"slug":72,"name":73,"color":48},{"slug":69,"name":70,"color":48},{"assessments":75},{"slug":539},{"id":558,"slug":434,"title":435,"subtitle":48,"excerpt":559,"coverUrl":560,"locale":13,"readingMinutes":428,"publishedAt":436,"viewCount":561,"likeCount":19,"commentCount":19,"author":562,"vertical":563,"topic":564,"tags":565,"_count":570,"playground":571,"hasQuiz":17,"hasPlayground":17},"01a08734-5c8d-73c5-95d1-e6b4ae366c25","How React 19 form Actions manage pending state automatically — useActionState, useFormStatus, and useOptimistic explained with the bug they replace.","\u002Fmedia\u002Fcovers\u002Freact-weekly-form-actions-pending-state.png",680,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[566,567,568,569],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":434},{"id":573,"slug":574,"title":575,"subtitle":48,"excerpt":576,"coverUrl":577,"locale":13,"readingMinutes":51,"publishedAt":578,"viewCount":579,"likeCount":19,"commentCount":19,"author":580,"vertical":581,"topic":582,"tags":583,"_count":588,"playground":589,"hasQuiz":17,"hasPlayground":17},"01a06d6c-41f6-77bd-95e3-cdd69a0f72eb","nextjs-weekly-server-actions-mutations-security","Next.js Server Actions: Mutations & Security (Cheat Sheet)","Next.js Server Actions look like plain functions but compile to public POST endpoints. Learn the mutation flow, built-in CSRF checks, and the auth you owe.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-server-actions-mutations-security.png","2026-09-08T10:57:00.975Z",361,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":548,"name":549},[584,585,586,587],{"slug":548,"name":549,"color":48},{"slug":61,"name":62,"color":48},{"slug":72,"name":73,"color":48},{"slug":69,"name":70,"color":48},{"assessments":75},{"slug":574},{"id":591,"slug":430,"title":431,"subtitle":48,"excerpt":592,"coverUrl":593,"locale":13,"readingMinutes":428,"publishedAt":432,"viewCount":594,"likeCount":19,"commentCount":19,"author":595,"vertical":596,"topic":597,"tags":598,"_count":603,"playground":604,"hasQuiz":17,"hasPlayground":17},"01a0632c-7248-73e3-9951-e8c6eaa578c7","React Compiler 1.0 is stable. Here's the re-render model it automates, which useMemo\u002FuseCallback\u002Fmemo calls you can delete, and what still needs you.","\u002Fmedia\u002Fcovers\u002Freact-weekly-compiler-memoization.png",382,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[599,600,601,602],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":430},{"page":75,"perPage":489,"total":51,"totalPages":43},"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F The \"wrong way first\" — wiring the native API up by hand inside a React app\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">selectPhoto\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">!\u003C\u002Fspan>document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>startViewTransition\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">setSelectedId\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F no support: just update, no animation\n\u003C\u002Fspan>    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n  document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">startViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">setSelectedId\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F ⚠️ setState is async — the DOM hasn't changed yet\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> startTransition\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useState \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Gallery\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>selectedId\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setSelectedId\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">null\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\n      \u003Cspan class=\"shj-class\">onClick\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n        \u003Cspan class=\"shj-cmnt\">\u002F\u002F The state change is wrapped in a Transition — this is what\n\u003C\u002Fspan>        \u003Cspan class=\"shj-cmnt\">\u002F\u002F lets any &lt;ViewTransition&gt; below react to it at all.\n\u003C\u002Fspan>        \u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setSelectedId\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">42\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n      \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n    \u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      Open photo\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> startTransition\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useState \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Notice\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> message \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan> \u003Cspan class=\"shj-class\">enter\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"slide-in\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">exit\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"fade-out\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-class\">className\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"notice\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>message\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">App\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>notice\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setNotice\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">null\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\n        \u003Cspan class=\"shj-class\">onClick\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setNotice\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Saved!\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        Save\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>notice \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Notice\u003C\u002Fspan> \u003Cspan class=\"shj-class\">message\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>notice\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-css shj-multiline\" data-lang=\"css\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-var\">::view-transition-new\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-var\">.slide-in\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">animation\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">slide-in\u003C\u002Fspan> \u003Cspan class=\"shj-num\">200\u003C\u002Fspan>ms \u003Cspan class=\"shj-num\">ease-out\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">@keyframes\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">slide-in\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-var\">transform\u003C\u002Fspan>: \u003Cspan class=\"shj-func\">translateY\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>-\u003Cspan class=\"shj-num\">12\u003C\u002Fspan>\u003Cspan class=\"shj-var\">px\u003C\u002Fspan>\u003Cspan class=\"shj-num\">\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-var\">opacity\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">0\u003C\u002Fspan>; \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ExpandableCard\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> expanded\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> onToggle\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> summary\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> detail \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan> \u003Cspan class=\"shj-class\">update\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"auto\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-class\">className\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"card\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">onClick\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>onToggle\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>summary\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>expanded \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan> \u003Cspan class=\"shj-class\">className\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"detail\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>detail\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Thumbnail\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> photo\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> onSelect \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`photo-\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>photo\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>id\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">img\u003C\u002Fspan>\n        \u003Cspan class=\"shj-class\">src\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>photo\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>thumbUrl\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n        \u003Cspan class=\"shj-class\">onClick\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">onSelect\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>photo\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">DetailView\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> photo \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`photo-\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>photo\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>id\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">img\u003C\u002Fspan> \u003Cspan class=\"shj-class\">src\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>photo\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>fullUrl\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-class\">className\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"detail-image\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> addTransitionType\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> startTransition \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">goToNext\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">addTransitionType\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"nav-forward\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-func\">setPage\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>p\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> p \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">goToPrevious\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">addTransitionType\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"nav-back\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-func\">setPage\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>p\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> p \u003Cspan class=\"shj-oper\">-\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-css shj-multiline\" data-lang=\"css\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-var\">:root\u003C\u002Fspan>\u003Cspan class=\"shj-var\">:active-view-transition-type\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">nav-back\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  &#38;\u003Cspan class=\"shj-var\">::view-transition-old\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-var\">.card\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">animation\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">slide-out-right\u003C\u002Fspan> \u003Cspan class=\"shj-num\">200\u003C\u002Fspan>ms;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003Cdiv>24\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-class\">ViewTransition\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  startTransition\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  addTransitionType\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 1. The state change MUST go through a Transition, or nothing animates.\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setSelectedId\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 2. Pick the shape:\n\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan> \u003Cspan class=\"shj-class\">enter\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"auto\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">exit\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"auto\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>      \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F* mounts \u002F unmounts *\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan> \u003Cspan class=\"shj-class\">update\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"auto\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>                 \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F* stays, resizes\u002Fmoves *\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ViewTransition\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`item-\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>id\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>           \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F* shared element pair  *\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 3. Style the browser pseudo-elements React targets for you:\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F ::view-transition-old(&lt;class-or-name&gt;)\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F ::view-transition-new(&lt;class-or-name&gt;)\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 4. Label WHY, to branch the CSS on cause:\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">addTransitionType\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"nav-back\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-func\">setPage\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>p \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> p \u003Cspan class=\"shj-oper\">-\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F :root:active-view-transition-type(nav-back) { &#38;::view-transition-old(.card) { … } }\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":616},[]]