container. For example, let’s say the parent component passes three
tags as the children prop to RowList: This is the first item.
This is the second item.
This is the third item.
Then, with the RowList implementation above, the final rendered result will look like this:
Children.map is similar to to transforming arrays with map(). The difference is that the children data structure is considered opaque. This means that even if it’s sometimes an array, you should not assume it’s an array or any other particular data type. This is why you should use Children.map if you need to transform it. App.jsRowList.jsRowList.jsReloadClearForkimport { Children } from 'react'; export default function RowList({ children }) { return (
{Children.map(children, child =>
{child}
)}
); } Deep DiveWhy is the children prop not always an array? Show DetailsIn React, the children prop is considered an opaque data structure. This means that you shouldn’t rely on how it is structured. To transform, filter, or count children, you should use the Children methods.In practice, the children data structure is often represented as an array internally. However, if there is only a single child, then React won’t create an extra array since this would lead to unnecessary memory overhead. As long as you use the Children methods instead of directly introspecting the children prop, your code will not break even if React changes how the data structure is actually implemented.Even when children is an array, Children.map has useful special behavior. For example, Children.map combines the keys on the returned elements with the keys on the children you’ve passed to it. This ensures the original JSX children don’t “lose” keys even if they get wrapped like in the example above. PitfallThe children data structure does not include rendered output of the components you pass as JSX. In the example below, the children received by the RowList only contains two items rather than three:
This is the first item.
This is why only two row wrappers are generated in this example:App.jsRowList.jsApp.jsReloadClearForkimport RowList from './RowList.js'; export default function App() { return (
This is the first item.
); } function MoreRows() { return ( <>
This is the second item.
This is the third item.
> ); } Show moreThere is no way to get the rendered output of an inner component like
when manipulating children. This is why it’s usually better to use one of the alternative solutions. Running some code for each child Call Children.forEach to iterate over each child in the children data structure. It does not return any value and is similar to the array forEach method. You can use it to run custom logic like constructing your own array. App.jsSeparatorList.jsSeparatorList.jsReloadClearForkimport { Children } from 'react'; export default function SeparatorList({ children }) { const result = []; Children.forEach(children, (child, index) => { result.push(child); result.push(
); }); result.pop(); // Remove the last separator return result; } PitfallAs mentioned earlier, there is no way to get the rendered output of an inner component when manipulating children. This is why it’s usually better to use one of the alternative solutions. Counting children Call Children.count(children) to calculate the number of children. App.jsRowList.jsRowList.jsReloadClearForkimport { Children } from 'react'; export default function RowList({ children }) { return (
Total rows: {Children.count(children)} {Children.map(children, child =>
{child}
)}
); } Show more PitfallAs mentioned earlier, there is no way to get the rendered output of an inner component when manipulating children. This is why it’s usually better to use one of the alternative solutions. Converting children to an array Call Children.toArray(children) to turn the children data structure into a regular JavaScript array. This lets you manipulate the array with built-in array methods like filter, sort, or reverse. App.jsReversedList.jsReversedList.jsReloadClearForkimport { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; } PitfallAs mentioned earlier, there is no way to get the rendered output of an inner component when manipulating children. This is why it’s usually better to use one of the alternative solutions. Alternatives NoteThis section describes alternatives to the Children API (with capital C) that’s imported like this:import { Children } from 'react';Don’t confuse it with using the children prop (lowercase c), which is good and encouraged. Exposing multiple components Manipulating children with the Children methods often leads to fragile code. When you pass children to a component in JSX, you don’t usually expect the component to manipulate or transform the individual children. When you can, try to avoid using the Children methods. For example, if you want every child of RowList to be wrapped in
, export a Row component, and manually wrap every row into it like this: App.jsRowList.jsApp.jsReloadClearForkimport { RowList, Row } from './RowList.js'; export default function App() { return (
This is the first item.
This is the second item.
This is the third item.
); } Show more Unlike using Children.map, this approach does not wrap every child automatically. However, this approach has a significant benefit compared to the earlier example with Children.map because it works even if you keep extracting more components. For example, it still works if you extract your own MoreRows component: App.jsRowList.jsApp.jsReloadClearForkimport { RowList, Row } from './RowList.js'; export default function App() { return (
This is the first item.
); } function MoreRows() { return ( <>
This is the second item.
This is the third item.
> ); } Show more This wouldn’t work with Children.map because it would “see”
as a single child (and a single row). Accepting an array of objects as a prop You can also explicitly pass an array as a prop. For example, this RowList accepts a rows array as a prop: App.jsRowList.jsApp.jsReloadClearForkimport { RowList, Row } from './RowList.js'; export default function App() { return (
This is the first item. }, { id: 'second', content: This is the second item.
}, { id: 'third', content: This is the third item.
} ]} /> ); } Since rows is a regular JavaScript array, the RowList component can use built-in array methods like map on it. This pattern is especially useful when you want to be able to pass more information as structured data together with children. In the below example, the TabSwitcher component receives an array of objects as the tabs prop: App.jsTabSwitcher.jsApp.jsReloadClearForkimport TabSwitcher from './TabSwitcher.js'; export default function App() { return ( This is the first item. }, { id: 'second', header: 'Second', content: This is the second item.
}, { id: 'third', header: 'Third', content: This is the third item.
} ]} /> ); } Show more Unlike passing the children as JSX, this approach lets you associate some extra data like header with each item. Because you are working with the tabs directly, and it is an array, you do not need the Children methods. Calling a render prop to customize rendering Instead of producing JSX for every single item, you can also pass a function that returns JSX, and call that function when necessary. In this example, the App component passes a renderContent function to the TabSwitcher component. The TabSwitcher component calls renderContent only for the selected tab: App.jsTabSwitcher.jsApp.jsReloadClearForkimport TabSwitcher from './TabSwitcher.js'; export default function App() { return ( { return tabId[0].toUpperCase() + tabId.slice(1); }} renderContent={tabId => { return This is the {tabId} item.
; }} /> ); } A prop like renderContent is called a render prop because it is a prop that specifies how to render a piece of the user interface. However, there is nothing special about it: it is a regular prop which happens to be a function. Render props are functions, so you can pass information to them. For example, this RowList component passes the id and the index of each row to the renderRow render prop, which uses index to highlight even rows: App.jsRowList.jsApp.jsReloadClearForkimport { RowList, Row } from './RowList.js'; export default function App() { return ( { return ( This is the {id} item.
); }} /> ); } Show more This is another example of how parent and child components can cooperate without manipulating the children. Troubleshooting I pass a custom component, but the Children methods don’t show its render result Suppose you pass two children to RowList like this: First item
If you do Children.count(children) inside RowList, you will get 2. Even if MoreRows renders 10 different items, or if it returns null, Children.count(children) will still be 2. From the RowList’s perspective, it only “sees” the JSX it has received. It does not “see” the internals of the MoreRows component. The limitation makes it hard to extract a component. This is why alternatives are preferred to using Children.PreviousLegacy React APIsNextcloneElement
```
Children
```
**Pattern 3:** Deep DiveWhy is the children prop not always an array? Show DetailsIn React, the children prop is considered an opaque data structure. This means that you shouldn’t rely on how it is structured. To transform, filter, or count children, you should use the Children methods.In practice, the children data structure is often represented as an array internally. However, if there is only a single child, then React won’t create an extra array since this would lead to unnecessary memory overhead. As long as you use the Children methods instead of directly introspecting the children prop, your code will not break even if React changes how the data structure is actually implemented.Even when children is an array, Children.map has useful special behavior. For example, Children.map combines the keys on the returned elements with the keys on the children you’ve passed to it. This ensures the original JSX children don’t “lose” keys even if they get wrapped like in the example above. PitfallThe children data structure does not include rendered output of the components you pass as JSX. In the example below, the children received by the RowList only contains two items rather than three: This is the first item.
This is why only two row wrappers are generated in this example:App.jsRowList.jsApp.jsReloadClearForkimport RowList from './RowList.js'; export default function App() { return ( This is the first item.
); } function MoreRows() { return ( <> This is the second item.
This is the third item.
> ); } Show moreThere is no way to get the rendered output of an inner component like when manipulating children. This is why it’s usually better to use one of the alternative solutions. Running some code for each child Call Children.forEach to iterate over each child in the children data structure. It does not return any value and is similar to the array forEach method. You can use it to run custom logic like constructing your own array.
```
children
```
**Pattern 4:** Learn ReactEscape HatchesManipulating the DOM with RefsReact automatically updates the DOM to match your render output, so your components won’t often need to manipulate it. However, sometimes you might need access to the DOM elements managed by React—for example, to focus a node, scroll to it, or measure its size and position. There is no built-in way to do those things in React, so you will need a ref to the DOM node. You will learn How to access a DOM node managed by React with the ref attribute How the ref JSX attribute relates to the useRef Hook How to access another component’s DOM node In which cases it’s safe to modify the DOM managed by React Getting a ref to the node To access a DOM node managed by React, first, import the useRef Hook: import { useRef } from 'react'; Then, use it to declare a ref inside your component: const myRef = useRef(null); Finally, pass your ref as the ref attribute to the JSX tag for which you want to get the DOM node: The useRef Hook returns an object with a single property called current. Initially, myRef.current will be null. When React creates a DOM node for this
, React will put a reference to this node into myRef.current. You can then access this DOM node from your event handlers and use the built-in browser APIs defined on it. // You can use any browser APIs, for example:myRef.current.scrollIntoView(); Example: Focusing a text input In this example, clicking the button will focus the input: App.jsApp.jsReloadClearForkimport { useRef } from 'react'; export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <>
Focus the input > ); } Show more To implement this: Declare inputRef with the useRef Hook. Pass it as
. This tells React to put this
’s DOM node into inputRef.current. In the handleClick function, read the input DOM node from inputRef.current and call focus() on it with inputRef.current.focus(). Pass the handleClick event handler to
with onClick. While DOM manipulation is the most common use case for refs, the useRef Hook can be used for storing other things outside React, like timer IDs. Similarly to state, refs remain between renders. Refs are like state variables that don’t trigger re-renders when you set them. Read about refs in Referencing Values with Refs. Example: Scrolling to an element You can have more than a single ref in a component. In this example, there is a carousel of three images. Each button centers an image by calling the browser scrollIntoView() method on the corresponding DOM node: App.jsApp.jsReloadClearForkimport { useRef } from 'react'; export default function CatFriends() { const firstCatRef = useRef(null); const secondCatRef = useRef(null); const thirdCatRef = useRef(null); function handleScrollToFirstCat() { firstCatRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } function handleScrollToSecondCat() { secondCatRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } function handleScrollToThirdCat() { thirdCatRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } return ( <> Neo Millie Bella > ); } Show more Deep DiveHow to manage a list of refs using a ref callback Show DetailsIn the above examples, there is a predefined number of refs. However, sometimes you might need a ref to each item in the list, and you don’t know how many you will have. Something like this wouldn’t work: {items.map((item) => { // Doesn't work! const ref = useRef(null); return ; })} This is because Hooks must only be called at the top-level of your component. You can’t call useRef in a loop, in a condition, or inside a map() call.One possible way around this is to get a single ref to their parent element, and then use DOM manipulation methods like querySelectorAll to “find” the individual child nodes from it. However, this is brittle and can break if your DOM structure changes.Another solution is to pass a function to the ref attribute. This is called a ref callback. React will call your ref callback with the DOM node when it’s time to set the ref, and call the cleanup function returned from the callback when it’s time to clear it. This lets you maintain your own array or a Map, and access any ref by its index or some kind of ID.This example shows how you can use this approach to scroll to an arbitrary node in a long list:App.jsApp.jsReloadClearForkimport { useRef, useState } from "react"; export default function CatFriends() { const itemsRef = useRef(null); const [catList, setCatList] = useState(setupCatList); function scrollToCat(cat) { const map = getMap(); const node = map.get(cat); node.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center", }); } function getMap() { if (!itemsRef.current) { // Initialize the Map on first usage. itemsRef.current = new Map(); } return itemsRef.current; } return ( <> scrollToCat(catList[0])}>Neo scrollToCat(catList[5])}>Millie scrollToCat(catList[8])}>Bella {catList.map((cat) => ( { const map = getMap(); map.set(cat, node); return () => { map.delete(cat); }; }} > ))} > ); } function setupCatList() { const catCount = 10; const catList = new Array(catCount) for (let i = 0; i < catCount; i++) { let imageUrl = ''; if (i < 5) { imageUrl = "https://placecats.com/neo/320/240"; } else if (i < 8) { imageUrl = "https://placecats.com/millie/320/240"; } else { imageUrl = "https://placecats.com/bella/320/240"; } catList[i] = { id: i, imageUrl, }; } return catList; } Show moreIn this example, itemsRef doesn’t hold a single DOM node. Instead, it holds a Map from item ID to a DOM node. (Refs can hold any values!) The ref callback on every list item takes care to update the Map: { const map = getMap(); // Add to the Map map.set(cat, node); return () => { // Remove from the Map map.delete(cat); }; }}>This lets you read individual DOM nodes from the Map later.NoteWhen Strict Mode is enabled, ref callbacks will run twice in development.Read more about how this helps find bugs in callback refs. Accessing another component’s DOM nodes PitfallRefs are an escape hatch. Manually manipulating another component’s DOM nodes can make your code fragile. You can pass refs from parent component to child components just like any other prop. import { useRef } from 'react';function MyInput({ ref }) { return ;}function MyForm() { const inputRef = useRef(null); return } In the above example, a ref is created in the parent component, MyForm, and is passed to the child component, MyInput. MyInput then passes the ref to . Because is a built-in component React sets the .current property of the ref to the DOM element. The inputRef created in MyForm now points to the DOM element returned by MyInput. A click handler created in MyForm can access inputRef and call focus() to set the focus on . App.jsApp.jsReloadClearForkimport { useRef } from 'react'; function MyInput({ ref }) { return ; } export default function MyForm() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> Focus the input > ); } Show more Deep DiveExposing a subset of the API with an imperative handle Show DetailsIn the above example, the ref passed to MyInput is passed on to the original DOM input element. This lets the parent component call focus() on it. However, this also lets the parent component do something else—for example, change its CSS styles. In uncommon cases, you may want to restrict the exposed functionality. You can do that with useImperativeHandle:App.jsApp.jsReloadClearForkimport { useRef, useImperativeHandle } from "react"; function MyInput({ ref }) { const realInputRef = useRef(null); useImperativeHandle(ref, () => ({ // Only expose focus and nothing else focus() { realInputRef.current.focus(); }, })); return ; }; export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> Focus the input > ); } Show moreHere, realInputRef inside MyInput holds the actual input DOM node. However, useImperativeHandle instructs React to provide your own special object as the value of a ref to the parent component. So inputRef.current inside the Form component will only have the focus method. In this case, the ref “handle” is not the DOM node, but the custom object you create inside useImperativeHandle call. When React attaches the refs In React, every update is split in two phases: During render, React calls your components to figure out what should be on the screen. During commit, React applies changes to the DOM. In general, you don’t want to access refs during rendering. That goes for refs holding DOM nodes as well. During the first render, the DOM nodes have not yet been created, so ref.current will be null. And during the rendering of updates, the DOM nodes haven’t been updated yet. So it’s too early to read them. React sets ref.current during the commit. Before updating the DOM, React sets the affected ref.current values to null. After updating the DOM, React immediately sets them to the corresponding DOM nodes. Usually, you will access refs from event handlers. If you want to do something with a ref, but there is no particular event to do it in, you might need an Effect. We will discuss Effects on the next pages. Deep DiveFlushing state updates synchronously with flushSync Show DetailsConsider code like this, which adds a new todo and scrolls the screen down to the last child of the list. Notice how, for some reason, it always scrolls to the todo that was just before the last added one:App.jsApp.jsReloadClearForkimport { useState, useRef } from 'react'; export default function TodoList() { const listRef = useRef(null); const [text, setText] = useState(''); const [todos, setTodos] = useState( initialTodos ); function handleAdd() { const newTodo = { id: nextId++, text: text }; setText(''); setTodos([ ...todos, newTodo]); listRef.current.lastChild.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } return ( <> Add setText(e.target.value)} /> {todos.map(todo => ( {todo.text} ))} > ); } let nextId = 0; let initialTodos = []; for (let i = 0; i < 20; i++) { initialTodos.push({ id: nextId++, text: 'Todo #' + (i + 1) }); } Show moreThe issue is with these two lines:setTodos([ ...todos, newTodo]);listRef.current.lastChild.scrollIntoView();In React, state updates are queued. Usually, this is what you want. However, here it causes a problem because setTodos does not immediately update the DOM. So the time you scroll the list to its last element, the todo has not yet been added. This is why scrolling always “lags behind” by one item.To fix this issue, you can force React to update (“flush”) the DOM synchronously. To do this, import flushSync from react-dom and wrap the state update into a flushSync call:flushSync(() => { setTodos([ ...todos, newTodo]);});listRef.current.lastChild.scrollIntoView();This will instruct React to update the DOM synchronously right after the code wrapped in flushSync executes. As a result, the last todo will already be in the DOM by the time you try to scroll to it:App.jsApp.jsReloadClearForkimport { useState, useRef } from 'react'; import { flushSync } from 'react-dom'; export default function TodoList() { const listRef = useRef(null); const [text, setText] = useState(''); const [todos, setTodos] = useState( initialTodos ); function handleAdd() { const newTodo = { id: nextId++, text: text }; flushSync(() => { setText(''); setTodos([ ...todos, newTodo]); }); listRef.current.lastChild.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } return ( <> Add setText(e.target.value)} /> {todos.map(todo => ( {todo.text} ))} > ); } let nextId = 0; let initialTodos = []; for (let i = 0; i < 20; i++) { initialTodos.push({ id: nextId++, text: 'Todo #' + (i + 1) }); } Show more Best practices for DOM manipulation with refs Refs are an escape hatch. You should only use them when you have to “step outside React”. Common examples of this include managing focus, scroll position, or calling browser APIs that React does not expose. If you stick to non-destructive actions like focusing and scrolling, you shouldn’t encounter any problems. However, if you try to modify the DOM manually, you can risk conflicting with the changes React is making. To illustrate this problem, this example includes a welcome message and two buttons. The first button toggles its presence using conditional rendering and state, as you would usually do in React. The second button uses the remove() DOM API to forcefully remove it from the DOM outside of React’s control. Try pressing “Toggle with setState” a few times. The message should disappear and appear again. Then press “Remove from the DOM”. This will forcefully remove it. Finally, press “Toggle with setState”: App.jsApp.jsReloadClearForkimport { useState, useRef } from 'react'; export default function Counter() { const [show, setShow] = useState(true); const ref = useRef(null); return ( { setShow(!show); }}> Toggle with setState { ref.current.remove(); }}> Remove from the DOM {show &&
Hello world
}
); } Show more After you’ve manually removed the DOM element, trying to use setState to show it again will lead to a crash. This is because you’ve changed the DOM, and React doesn’t know how to continue managing it correctly. Avoid changing DOM nodes managed by React. Modifying, adding children to, or removing children from elements that are managed by React can lead to inconsistent visual results or crashes like above. However, this doesn’t mean that you can’t do it at all. It requires caution. You can safely modify parts of the DOM that React has no reason to update. For example, if some is always empty in the JSX, React won’t have a reason to touch its children list. Therefore, it is safe to manually add or remove elements there. Recap Refs are a generic concept, but most often you’ll use them to hold DOM elements. You instruct React to put a DOM node into myRef.current by passing
. Usually, you will use refs for non-destructive actions like focusing, scrolling, or measuring DOM elements. A component doesn’t expose its DOM nodes by default. You can opt into exposing a DOM node by using the ref prop. Avoid changing DOM nodes managed by React. If you do modify DOM nodes managed by React, modify parts that React has no reason to update. Try out some challenges1. Play and pause the video 2. Focus the search field 3. Scrolling an image carousel 4. Focus the search field with separate components Challenge 1 of 4: Play and pause the video In this example, the button toggles a state variable to switch between a playing and a paused state. However, in order to actually play or pause the video, toggling state is not enough. You also need to call play() and pause() on the DOM element for the
. Add a ref to it, and make the button work.App.jsApp.jsReloadClearForkimport { useState, useRef } from 'react'; export default function VideoPlayer() { const [isPlaying, setIsPlaying] = useState(false); function handleClick() { const nextIsPlaying = !isPlaying; setIsPlaying(nextIsPlaying); } return ( <> {isPlaying ? 'Pause' : 'Play'} > ) } Show moreFor an extra challenge, keep the “Play” button in sync with whether the video is playing even if the user right-clicks the video and plays it using the built-in browser media controls. You might want to listen to onPlay and onPause on the video to do that. Show solutionNext ChallengePreviousReferencing Values with RefsNextSynchronizing with Effects
```
ref
```
**Pattern 5:** // You can use any browser APIs, for example:myRef.current.scrollIntoView();
```
// You can use any browser APIs, for example:myRef.current.scrollIntoView();
```
**Pattern 6:** API ReferenceComponents lets you hide and restore the UI and internal state of its children. Reference Usage Restoring the state of hidden components Restoring the DOM of hidden components Pre-rendering content that’s likely to become visible Speeding up interactions during page load Troubleshooting My hidden components have unwanted side effects My hidden components have Effects that aren’t running Reference You can use Activity to hide part of your application: When an Activity boundary is hidden, React will visually hide its children using the display: "none" CSS property. It will also destroy their Effects, cleaning up any active subscriptions. While hidden, children still re-render in response to new props, albeit at a lower priority than the rest of the content. When the boundary becomes visible again, React will reveal the children with their previous state restored, and re-create their Effects. In this way, Activity can be thought of as a mechanism for rendering “background activity”. Rather than completely discarding content that’s likely to become visible again, you can use Activity to maintain and restore that content’s UI and internal state, while ensuring that your hidden content has no unwanted side effects. See more examples below. Props children: The UI you intend to show and hide. mode: A string value of either 'visible' or 'hidden'. If omitted, defaults to 'visible'. Caveats If an Activity is rendered inside of a ViewTransition, and it becomes visible as a result of an update caused by startTransition, it will activate the ViewTransition’s enter animation. If it becomes hidden, it will activate its exit animation. An Activity that just renders text will not render anything rather than rendering hidden text, because there’s no corresponding DOM element to apply visibility changes to. For example, will not produce any output in the DOM for const ComponentThatJustReturnsText = () => "Hello, World!". Usage Restoring the state of hidden components In React, when you want to conditionally show or hide a component, you typically mount or unmount it based on that condition: {isShowingSidebar && ( )} But unmounting a component destroys its internal state, which is not always what you want. When you hide a component using an Activity boundary instead, React will “save” its state for later: This makes it possible to hide and then later restore components in the state they were previously in. The following example has a sidebar with an expandable section. You can press “Overview” to reveal the three subitems below it. The main app area also has a button that hides and shows the sidebar. Try expanding the Overview section, and then toggling the sidebar closed then open: App.jsSidebar.jsApp.jsReloadClearForkimport { useState } from 'react'; import Sidebar from './Sidebar.js'; export default function App() { const [isShowingSidebar, setIsShowingSidebar] = useState(true); return ( <> {isShowingSidebar && ( )} setIsShowingSidebar(!isShowingSidebar)}> Toggle sidebar Main content > ); } Show more The Overview section always starts out collapsed. Because we unmount the sidebar when isShowingSidebar flips to false, all its internal state is lost. This is a perfect use case for Activity. We can preserve the internal state of our sidebar, even when visually hiding it. Let’s replace the conditional rendering of our sidebar with an Activity boundary: // Before{isShowingSidebar && ( )}// After and check out the new behavior: App.jsSidebar.jsApp.jsReloadClearForkimport { Activity, useState } from 'react'; import Sidebar from './Sidebar.js'; export default function App() { const [isShowingSidebar, setIsShowingSidebar] = useState(true); return ( <> setIsShowingSidebar(!isShowingSidebar)}> Toggle sidebar Main content > ); } Show more Our sidebar’s internal state is now restored, without any changes to its implementation. Restoring the DOM of hidden components Since Activity boundaries hide their children using display: none, their children’s DOM is also preserved when hidden. This makes them great for maintaining ephemeral state in parts of the UI that the user is likely to interact with again. In this example, the Contact tab has a