
react
by EF-Cosmos
Interactive mathematical expression derivation and learning platform
SKILL.md
name: react description: React framework for building user interfaces. Use for React components, hooks, state management, JSX, and modern frontend development.
React Skill
React framework for building user interfaces. use for react components, hooks, state management, jsx, and modern frontend development., generated from official documentation.
When to Use This Skill
This skill should be triggered when:
- Working with react
- Asking about react features or APIs
- Implementing react solutions
- Debugging react code
- Learning react best practices
Quick Reference
Common Patterns
Pattern 1: API ReferenceHooksuseSyncExternalStoreuseSyncExternalStore is a React Hook that lets you subscribe to an external store.const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?) Reference useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?) Usage Subscribing to an external store Subscribing to a browser API Extracting the logic to a custom Hook Adding support for server rendering Troubleshooting I’m getting an error: “The result of getSnapshot should be cached” My subscribe function gets called after every re-render Reference useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?) Call useSyncExternalStore at the top level of your component to read a value from an external data store. import { useSyncExternalStore } from 'react';import { todosStore } from './todoStore.js';function TodosApp() { const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot); // ...} It returns the snapshot of the data in the store. You need to pass two functions as arguments: The subscribe function should subscribe to the store and return a function that unsubscribes. The getSnapshot function should read a snapshot of the data from the store. See more examples below. Parameters subscribe: A function that takes a single callback argument and subscribes it to the store. When the store changes, it should invoke the provided callback, which will cause React to re-call getSnapshot and (if needed) re-render the component. The subscribe function should return a function that cleans up the subscription. getSnapshot: A function that returns a snapshot of the data in the store that’s needed by the component. While the store has not changed, repeated calls to getSnapshot must return the same value. If the store changes and the returned value is different (as compared by Object.is), React re-renders the component. optional getServerSnapshot: A function that returns the initial snapshot of the data in the store. It will be used only during server rendering and during hydration of server-rendered content on the client. The server snapshot must be the same between the client and the server, and is usually serialized and passed from the server to the client. If you omit this argument, rendering the component on the server will throw an error. Returns The current snapshot of the store which you can use in your rendering logic. Caveats The store snapshot returned by getSnapshot must be immutable. If the underlying store has mutable data, return a new immutable snapshot if the data has changed. Otherwise, return a cached last snapshot. If a different subscribe function is passed during a re-render, React will re-subscribe to the store using the newly passed subscribe function. You can prevent this by declaring subscribe outside the component. If the store is mutated during a non-blocking Transition update, React will fall back to performing that update as blocking. Specifically, for every Transition update, React will call getSnapshot a second time just before applying changes to the DOM. If it returns a different value than when it was called originally, React will restart the update from scratch, this time applying it as a blocking update, to ensure that every component on screen is reflecting the same version of the store. It’s not recommended to suspend a render based on a store value returned by useSyncExternalStore. The reason is that mutations to the external store cannot be marked as non-blocking Transition updates, so they will trigger the nearest Suspense fallback, replacing already-rendered content on screen with a loading spinner, which typically makes a poor UX. For example, the following are discouraged: const LazyProductDetailPage = lazy(() => import('./ProductDetailPage.js'));function ShoppingApp() { const selectedProductId = useSyncExternalStore(...); // ❌ Calling use with a Promise dependent on selectedProductId const data = use(fetchItem(selectedProductId)) // ❌ Conditionally rendering a lazy component based on selectedProductId return selectedProductId != null ? : ;} Usage Subscribing to an external store Most of your React components will only read data from their props, state, and context. However, sometimes a component needs to read some data from some store outside of React that changes over time. This includes: Third-party state management libraries that hold state outside of React. Browser APIs that expose a mutable value and events to subscribe to its changes. Call useSyncExternalStore at the top level of your component to read a value from an external data store. import { useSyncExternalStore } from 'react';import { todosStore } from './todoStore.js';function TodosApp() { const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot); // ...} It returns the snapshot of the data in the store. You need to pass two functions as arguments: The subscribe function should subscribe to the store and return a function that unsubscribes. The getSnapshot function should read a snapshot of the data from the store. React will use these functions to keep your component subscribed to the store and re-render it on changes. For example, in the sandbox below, todosStore is implemented as an external store that stores data outside of React. The TodosApp component connects to that external store with the useSyncExternalStore Hook. App.jstodoStore.jsApp.jsReloadClearForkimport { useSyncExternalStore } from 'react'; import { todosStore } from './todoStore.js'; export default function TodosApp() { const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot); return ( <> <button onClick={() => todosStore.addTodo()}>Add todo {todos.map(todo => ( {todo.text} ))} </> ); } Show more NoteWhen possible, we recommend using built-in React state with useState and useReducer instead. The useSyncExternalStore API is mostly useful if you need to integrate with existing non-React code. Subscribing to a browser API Another reason to add useSyncExternalStore is when you want to subscribe to some value exposed by the browser that changes over time. For example, suppose that you want your component to display whether the network connection is active. The browser exposes this information via a property called navigator.onLine. This value can change without React’s knowledge, so you should read it with useSyncExternalStore. import { useSyncExternalStore } from 'react';function ChatIndicator() { const isOnline = useSyncExternalStore(subscribe, getSnapshot); // ...} To implement the getSnapshot function, read the current value from the browser API: function getSnapshot() { return navigator.onLine;} Next, you need to implement the subscribe function. For example, when navigator.onLine changes, the browser fires the online and offline events on the window object. You need to subscribe the callback argument to the corresponding events, and then return a function that cleans up the subscriptions: function subscribe(callback) { window.addEventListener('online', callback); window.addEventListener('offline', callback); return () => { window.removeEventListener('online', callback); window.removeEventListener('offline', callback); };} Now React knows how to read the value from the external navigator.onLine API and how to subscribe to its changes. Disconnect your device from the network and notice that the component re-renders in response: App.jsApp.jsReloadClearForkimport { useSyncExternalStore } from 'react'; export default function ChatIndicator() { const isOnline = useSyncExternalStore(subscribe, getSnapshot); return {isOnline ? '✅ Online' : '❌ Disconnected'}; } function getSnapshot() { return navigator.onLine; } function subscribe(callback) { window.addEventListener('online', callback); window.addEventListener('offline', callback); return () => { window.removeEventListener('online', callback); window.removeEventListener('offline', callback); }; } Show more Extracting the logic to a custom Hook Usually you won’t write useSyncExternalStore directly in your components. Instead, you’ll typically call it from your own custom Hook. This lets you use the same external store from different components. For example, this custom useOnlineStatus Hook tracks whether the network is online: import { useSyncExternalStore } from 'react';export function useOnlineStatus() { const isOnline = useSyncExternalStore(subscribe, getSnapshot); return isOnline;}function getSnapshot() { // ...}function subscribe(callback) { // ...} Now different components can call useOnlineStatus without repeating the underlying implementation: App.jsuseOnlineStatus.jsApp.jsReloadClearForkimport { useOnlineStatus } from './useOnlineStatus.js'; function StatusBar() { const isOnline = useOnlineStatus(); return {isOnline ? '✅ Online' : '❌ Disconnected'}; } function SaveButton() { const isOnline = useOnlineStatus(); function handleSaveClick() { console.log('✅ Progress saved'); } return ( {isOnline ? 'Save progress' : 'Reconnecting...'} ); } export default function App() { return ( <> </> ); } Show more Adding support for server rendering If your React app uses server rendering, your React components will also run outside the browser environment to generate the initial HTML. This creates a few challenges when connecting to an external store: If you’re connecting to a browser-only API, it won’t work because it does not exist on the server. If you’re connecting to a third-party data store, you’ll need its data to match between the server and client. To solve these issues, pass a getServerSnapshot function as the third argument to useSyncExternalStore: import { useSyncExternalStore } from 'react';export function useOnlineStatus() { const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); return isOnline;}function getSnapshot() { return navigator.onLine;}function getServerSnapshot() { return true; // Always show "Online" for server-generated HTML}function subscribe(callback) { // ...} The getServerSnapshot function is similar to getSnapshot, but it runs only in two situations: It runs on the server when generating the HTML. It runs on the client during hydration, i.e. when React takes the server HTML and makes it interactive. This lets you provide the initial snapshot value which will be used before the app becomes interactive. If there is no meaningful initial value for the server rendering, omit this argument to force rendering on the client. NoteMake sure that getServerSnapshot returns the same exact data on the initial client render as it returned on the server. For example, if getServerSnapshot returned some prepopulated store content on the server, you need to transfer this content to the client. One way to do this is to emit a tag during server rendering that sets a global like window.MY_STORE_DATA, and read from that global on the client in getServerSnapshot. Your external store should provide instructions on how to do that. Troubleshooting I’m getting an error: “The result of getSnapshot should be cached” This error means your getSnapshot function returns a new object every time it’s called, for example: function getSnapshot() { // 🔴 Do not return always different objects from getSnapshot return { todos: myStore.todos };} React will re-render the component if getSnapshot return value is different from the last time. This is why, if you always return a different value, you will enter an infinite loop and get this error. Your getSnapshot object should only return a different object if something has actually changed. If your store contains immutable data, you can return that data directly: function getSnapshot() { // ✅ You can return immutable data return myStore.todos;} If your store data is mutable, your getSnapshot function should return an immutable snapshot of it. This means it does need to create new objects, but it shouldn’t do this for every single call. Instead, it should store the last calculated snapshot, and return the same snapshot as the last time if the data in the store has not changed. How you determine whether mutable data has changed depends on your mutable store. My subscribe function gets called after every re-render This subscribe function is defined inside a component so it is different on every re-render: function ChatIndicator() { // 🚩 Always a different function, so React will resubscribe on every re-render function subscribe() { // ... } const isOnline = useSyncExternalStore(subscribe, getSnapshot); // ...} React will resubscribe to your store if you pass a different subscribe function between re-renders. If this causes performance issues and you’d like to avoid resubscribing, move the subscribe function outside: // ✅ Always the same function, so React won't need to resubscribefunction subscribe() { // ...}function ChatIndicator() { const isOnline = useSyncExternalStore(subscribe, getSnapshot); // ...} Alternatively, wrap subscribe into useCallback to only resubscribe when some argument changes: function ChatIndicator({ userId }) { // ✅ Same function as long as userId doesn't change const subscribe = useCallback(() => { // ... }, [userId]); const isOnline = useSyncExternalStore(subscribe, getSnapshot); // ...}PrevioususeStateNextuseTransition
useSyncExternalStore
Pattern 2: Adding support for server rendering If your React app uses server rendering, your React components will also run outside the browser environment to generate the initial HTML. This creates a few challenges when connecting to an external store: If you’re connecting to a browser-only API, it won’t work because it does not exist on the server. If you’re connecting to a third-party data store, you’ll need its data to match between the server and client. To solve these issues, pass a getServerSnapshot function as the third argument to useSyncExternalStore: import { useSyncExternalStore } from 'react';export function useOnlineStatus() { const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); return isOnline;}function getSnapshot() { return navigator.onLine;}function getServerSnapshot() { return true; // Always show "Online" for server-generated HTML}function subscribe(callback) { // ...} The getServerSnapshot function is similar to getSnapshot, but it runs only in two situations: It runs on the server when generating the HTML. It runs on the client during hydration, i.e. when React takes the server HTML and makes it interactive. This lets you provide the initial snapshot value which will be used before the app becomes interactive. If there is no meaningful initial value for the server rendering, omit this argument to force rendering on the client. NoteMake sure that getServerSnapshot returns the same exact data on the initial client render as it returned on the server. For example, if getServerSnapshot returned some prepopulated store content on the server, you need to transfer this content to the client. One way to do this is to emit a tag during server rendering that sets a global like window.MY_STORE_DATA, and read from that global on the client in getServerSnapshot. Your external store should provide instructions on how to do that. Troubleshooting I’m getting an error: “The result of getSnapshot should be cached” This error means your getSnapshot function returns a new object every time it’s called, for example: function getSnapshot() { // 🔴 Do not return always different objects from getSnapshot return { todos: myStore.todos };} React will re-render the component if getSnapshot return value is different from the last time. This is why, if you always return a different value, you will enter an infinite loop and get this error. Your getSnapshot object should only return a different object if something has actually changed. If your store contains immutable data, you can return that data directly: function getSnapshot() { // ✅ You can return immutable data return myStore.todos;} If your store data is mutable, your getSnapshot function should return an immutable snapshot of it. This means it does need to create new objects, but it shouldn’t do this for every single call. Instead, it should store the last calculated snapshot, and return the same snapshot as the last time if the data in the store has not changed. How you determine whether mutable data has changed depends on your mutable store. My subscribe function gets called after every re-render This subscribe function is defined inside a component so it is different on every re-render: function ChatIndicator() { // 🚩 Always a different function, so React will resubscribe on every re-render function subscribe() { // ... } const isOnline = useSyncExternalStore(subscribe, getSnapshot); // ...} React will resubscribe to your store if you pass a different subscribe function between re-renders. If this causes performance issues and you’d like to avoid resubscribing, move the subscribe function outside: // ✅ Always the same function, so React won't need to resubscribefunction subscribe() { // ...}function ChatIndicator() { const isOnline = useSyncExternalStore(subscribe, getSnapshot); // ...} Alternatively, wrap subscribe into useCallback to only resubscribe when some argument changes: function ChatIndicator({ userId }) { // ✅ Same function as long as userId doesn't change const subscribe = useCallback(() => { // ... }, [userId]); const isOnline = useSyncExternalStore(subscribe, getSnapshot); // ...}
getServerSnapshot
Pattern 3: This error means your getSnapshot function returns a new object every time it’s called, for example:
getSnapshot
Pattern 4: 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: <Activity mode={isShowingSidebar ? "visible" : "hidden"}> 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: <Activity mode={isShowingSidebar ? "visible" : "hidden"}> 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 && ( )} <button onClick={() => 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<Activity mode={isShowingSidebar ? 'visible' : 'hidden'}> 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 ( <> <Activity mode={isShowingSidebar ? 'visible' : 'hidden'}> <button onClick={() => 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 where the user can enter a message. If you enter some text, change to the Home tab, then change back to the Contact tab, the draft message is lost: App.jsTabButton.jsHome.jsContact.jsContact.jsReloadClearForkexport default function Contact() { return ( Send me a message! You can find me online here: admin@mysite.com +123456789 ); } This is because we’re fully unmounting Contact in App. When the Contact tab unmounts, the element’s internal DOM state is lost. If we switch to using an Activity boundary to show and hide the active tab, we can preserve the state of each tab’s DOM. Try entering text and switching tabs again, and you’ll see the draft message is no longer reset: App.jsTabButton.jsHome.jsContact.jsApp.jsReloadClearForkimport { Activity, useState } from 'react'; import TabButton from './TabButton.js'; import Home from './Home.js'; import Contact from './Contact.js'; export default function App() { const [activeTab, setActiveTab] = useState('contact'); return ( <> <TabButton isActive={activeTab === 'home'} onClick={() => setActiveTab('home')} > Home <TabButton isActive={activeTab === 'contact'} onClick={() => setActiveTab('contact')} > Contact <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}> <Activity mode={activeTab === 'contact' ? 'visible' : 'hidden'}> </> ); } Show more Again, the Activity boundary let us preserve the Contact tab’s internal state without changing its implementation. Pre-rendering content that’s likely to become visible So far, we’ve seen how Activity can hide some content that the user has interacted with, without discarding that content’s ephemeral state. But Activity boundaries can also be used to prepare content that the user has yet to see for the first time: When an Activity boundary is hidden during its initial render, its children won’t be visible on the page — but they will still be rendered, albeit at a lower priority than the visible content, and without mounting their Effects. This pre-rendering allows the children to load any code or data they need ahead of time, so that later, when the Activity boundary becomes visible, the children can appear faster with reduced loading times. Let’s look at an example. In this demo, the Posts tab loads some data. If you press it, you’ll see a Suspense fallback displayed while the data is being fetched: App.jsHome.jsPosts.jsApp.jsReloadClearForkimport { useState, Suspense } from 'react'; import TabButton from './TabButton.js'; import Home from './Home.js'; import Posts from './Posts.js'; export default function App() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton isActive={activeTab === 'home'} onClick={() => setActiveTab('home')} > Home <TabButton isActive={activeTab === 'posts'} onClick={() => setActiveTab('posts')} > Posts <Suspense fallback={🌀 Loading...}> {activeTab === 'home' && } {activeTab === 'posts' && } </> ); } Show more This is because App doesn’t mount Posts until its tab is active. If we update App to use an Activity boundary to show and hide the active tab, Posts will be pre-rendered when the app first loads, allowing it to fetch its data before it becomes visible. Try clicking the Posts tab now: App.jsHome.jsPosts.jsApp.jsReloadClearForkimport { Activity, useState, Suspense } from 'react'; import TabButton from './TabButton.js'; import Home from './Home.js'; import Posts from './Posts.js'; export default function App() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton isActive={activeTab === 'home'} onClick={() => setActiveTab('home')} > Home <TabButton isActive={activeTab === 'posts'} onClick={() => setActiveTab('posts')} > Posts <Suspense fallback={🌀 Loading...}> <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}> <Activity mode={activeTab === 'posts' ? 'visible' : 'hidden'}> </> ); } Show more Posts was able to prepare itself for a faster render, thanks to the hidden Activity boundary. Pre-rendering components with hidden Activity boundaries is a powerful way to reduce loading times for parts of the UI that the user is likely to interact with next. NoteOnly Suspense-enabled data sources will be fetched during pre-rendering. They include: Data fetching with Suspense-enabled frameworks like Relay and Next.js Lazy-loading component code with lazy Reading the value of a cached Promise with use Activity does not detect data that is fetched inside an Effect.The exact way you would load data in the Posts component above depends on your framework. If you use a Suspense-enabled framework, you’ll find the details in its data fetching documentation.Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. Speeding up interactions during page load React includes an under-the-hood performance optimization called Selective Hydration. It works by hydrating your app’s initial HTML in chunks, enabling some components to become interactive even if other components on the page haven’t loaded their code or data yet. Suspense boundaries participate in Selective Hydration, because they naturally divide your component tree into units that are independent from one another: function Page() { return ( <> </> )} Here, MessageComposer can be fully hydrated during the initial render of the page, even before Chats is mounted and starts to fetch its data. So by breaking up your component tree into discrete units, Suspense allows React to hydrate your app’s server-rendered HTML in chunks, enabling parts of your app to become interactive as fast as possible. But what about pages that don’t use Suspense? Take this tabs example: function Page() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton onClick={() => setActiveTab('home')}> Home <TabButton onClick={() => setActiveTab('video')}> Video {activeTab === 'home' && ( )} {activeTab === 'video' && ( )} </> )} Here, React must hydrate the entire page all at once. If Home or Video are slower to render, they could make the tab buttons feel unresponsive during hydration. Adding Suspense around the active tab would solve this: function Page() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton onClick={() => setActiveTab('home')}> Home <TabButton onClick={() => setActiveTab('video')}> Video <Suspense fallback={}> {activeTab === 'home' && ( )} {activeTab === 'video' && ( )} </> )} …but it would also change the UI, since the Placeholder fallback would be displayed on the initial render. Instead, we can use Activity. Since Activity boundaries show and hide their children, they already naturally divide the component tree into independent units. And just like Suspense, this feature allows them to participate in Selective Hydration. Let’s update our example to use Activity boundaries around the active tab: function Page() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton onClick={() => setActiveTab('home')}> Home <TabButton onClick={() => setActiveTab('video')}> Video <Activity mode={activeTab === "home" ? "visible" : "hidden"}> <Activity mode={activeTab === "video" ? "visible" : "hidden"}> </> )} Now our initial server-rendered HTML looks the same as it did in the original version, but thanks to Activity, React can hydrate the tab buttons first, before it even mounts Home or Video. Thus, in addition to hiding and showing content, Activity boundaries help improve your app’s performance during hydration by letting React know which parts of your page can become interactive in isolation. And even if your page doesn’t ever hide part of its content, you can still add always-visible Activity boundaries to improve hydration performance: function Page() { return ( <> </> );} Troubleshooting My hidden components have unwanted side effects An Activity boundary hides its content by setting display: none on its children and cleaning up any of their Effects. So, most well-behaved React components that properly clean up their side effects will already be robust to being hidden by Activity. But there are some situations where a hidden component behaves differently than an unmounted one. Most notably, since a hidden component’s DOM is not destroyed, any side effects from that DOM will persist, even after the component is hidden. As an example, consider a tag. Typically it doesn’t require any cleanup, because even if you’re playing a video, unmounting the tag stops the video and audio from playing in the browser. Try playing the video and then pressing Home in this demo: App.jsHome.jsVideo.jsApp.jsReloadClearForkimport { useState } from 'react'; import TabButton from './TabButton.js'; import Home from './Home.js'; import Video from './Video.js'; export default function App() { const [activeTab, setActiveTab] = useState('video'); return ( <> <TabButton isActive={activeTab === 'home'} onClick={() => setActiveTab('home')} > Home <TabButton isActive={activeTab === 'video'} onClick={() => setActiveTab('video')} > Video {activeTab === 'home' && } {activeTab === 'video' && } </> ); } Show more The video stops playing as expected. Now, let’s say we wanted to preserve the timecode where the user last watched, so that when they tab back to the video, it doesn’t start over from the beginning again. This is a great use case for Activity! Let’s update App to hide the inactive tab with a hidden Activity boundary instead of unmounting it, and see how the demo behaves this time: App.jsHome.jsVideo.jsApp.jsReloadClearForkimport { Activity, useState } from 'react'; import TabButton from './TabButton.js'; import Home from './Home.js'; import Video from './Video.js'; export default function App() { const [activeTab, setActiveTab] = useState('video'); return ( <> <TabButton isActive={activeTab === 'home'} onClick={() => setActiveTab('home')} > Home <TabButton isActive={activeTab === 'video'} onClick={() => setActiveTab('video')} > Video <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}> <Activity mode={activeTab === 'video' ? 'visible' : 'hidden'}> </> ); } Show more Whoops! The video and audio continue to play even after it’s been hidden, because the tab’s element is still in the DOM. To fix this, we can add an Effect with a cleanup function that pauses the video: export default function VideoTab() { const ref = useRef(); useLayoutEffect(() => { const videoRef = ref.current; return () => { videoRef.pause() } }, []); return ( );} We call useLayoutEffect instead of useEffect because conceptually the clean-up code is tied to the component’s UI being visually hidden. If we used a regular effect, the code could be delayed by (say) a re-suspending Suspense boundary or a View Transition. Let’s see the new behavior. Try playing the video, switching to the Home tab, then back to the Video tab: App.jsHome.jsVideo.jsApp.jsReloadClearForkimport { Activity, useState } from 'react'; import TabButton from './TabButton.js'; import Home from './Home.js'; import Video from './Video.js'; export default function App() { const [activeTab, setActiveTab] = useState('video'); return ( <> <TabButton isActive={activeTab === 'home'} onClick={() => setActiveTab('home')} > Home <TabButton isActive={activeTab === 'video'} onClick={() => setActiveTab('video')} > Video <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}> <Activity mode={activeTab === 'video' ? 'visible' : 'hidden'}> </> ); } Show more It works great! Our cleanup function ensures that the video stops playing if it’s ever hidden by an Activity boundary, and even better, because the tag is never destroyed, the timecode is preserved, and the video itself doesn’t need to be initialized or downloaded again when the user switches back to keep watching it. This is a great example of using Activity to preserve ephemeral DOM state for parts of the UI that become hidden, but the user is likely to interact with again soon. Our example illustrates that for certain tags like , unmounting and hiding have different behavior. If a component renders DOM that has a side effect, and you want to prevent that side effect when an Activity boundary hides it, add an Effect with a return function to clean it up. The most common cases of this will be from the following tags: Typically, though, most of your React components should already be robust to being hidden by an Activity boundary. And conceptually, you should think of “hidden” Activities as being unmounted. To eagerly discover other Effects that don’t have proper cleanup, which is important not only for Activity boundaries but for many other behaviors in React, we recommend using . My hidden components have Effects that aren’t running When an is “hidden”, all its children’s Effects are cleaned up. Conceptually, the children are unmounted, but React saves their state for later. This is a feature of Activity because it means subscriptions won’t be active for hidden parts of the UI, reducing the amount of work needed for hidden content. If you’re relying on an Effect mounting to clean up a component’s side effects, refactor the Effect to do the work in the returned cleanup function instead. To eagerly find problematic Effects, we recommend adding which will eagerly perform Activity unmounts and mounts to catch any unexpected side-effects.PreviousNext
<Activity>
Pattern 5: Posts was able to prepare itself for a faster render, thanks to the hidden Activity boundary. Pre-rendering components with hidden Activity boundaries is a powerful way to reduce loading times for parts of the UI that the user is likely to interact with next. NoteOnly Suspense-enabled data sources will be fetched during pre-rendering. They include: Data fetching with Suspense-enabled frameworks like Relay and Next.js Lazy-loading component code with lazy Reading the value of a cached Promise with use Activity does not detect data that is fetched inside an Effect.The exact way you would load data in the Posts component above depends on your framework. If you use a Suspense-enabled framework, you’ll find the details in its data fetching documentation.Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. Speeding up interactions during page load React includes an under-the-hood performance optimization called Selective Hydration. It works by hydrating your app’s initial HTML in chunks, enabling some components to become interactive even if other components on the page haven’t loaded their code or data yet. Suspense boundaries participate in Selective Hydration, because they naturally divide your component tree into units that are independent from one another: function Page() { return ( <> </> )} Here, MessageComposer can be fully hydrated during the initial render of the page, even before Chats is mounted and starts to fetch its data. So by breaking up your component tree into discrete units, Suspense allows React to hydrate your app’s server-rendered HTML in chunks, enabling parts of your app to become interactive as fast as possible. But what about pages that don’t use Suspense? Take this tabs example: function Page() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton onClick={() => setActiveTab('home')}> Home <TabButton onClick={() => setActiveTab('video')}> Video {activeTab === 'home' && ( )} {activeTab === 'video' && ( )} </> )} Here, React must hydrate the entire page all at once. If Home or Video are slower to render, they could make the tab buttons feel unresponsive during hydration. Adding Suspense around the active tab would solve this: function Page() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton onClick={() => setActiveTab('home')}> Home <TabButton onClick={() => setActiveTab('video')}> Video <Suspense fallback={}> {activeTab === 'home' && ( )} {activeTab === 'video' && ( )} </> )} …but it would also change the UI, since the Placeholder fallback would be displayed on the initial render. Instead, we can use Activity. Since Activity boundaries show and hide their children, they already naturally divide the component tree into independent units. And just like Suspense, this feature allows them to participate in Selective Hydration. Let’s update our example to use Activity boundaries around the active tab: function Page() { const [activeTab, setActiveTab] = useState('home'); return ( <> <TabButton onClick={() => setActiveTab('home')}> Home <TabButton onClick={() => setActiveTab('video')}> Video <Activity mode={activeTab === "home" ? "visible" : "hidden"}> <Activity mode={activeTab === "video" ? "visible" : "hidden"}> </> )} Now our initial server-rendered HTML looks the same as it did in the original version, but thanks to Activity, React can hydrate the tab buttons first, before it even mounts Home or Video. Thus, in addition to hiding and showing content, Activity boundaries help improve your app’s performance during hydration by letting React know which parts of your page can become interactive in isolation. And even if your page doesn’t ever hide part of its content, you can still add always-visible Activity boundaries to improve hydration performance: function Page() { return ( <> </> );} Troubleshooting My hidden components have unwanted side effects An Activity boundary hides its content by setting display: none on its children and cleaning up any of their Effects. So, most well-behaved React components that properly clean up their side effects will already be robust to being hidden by Activity. But there are some situations where a hidden component behaves differently than an unmounted one. Most notably, since a hidden component’s DOM is not destroyed, any side effects from that DOM will persist, even after the component is hidden. As an example, consider a tag. Typically it doesn’t require any cleanup, because even if you’re playing a video, unmounting the tag stops the video and audio from playing in the browser. Try playing the video and then pressing Home in this demo:
Posts
Pattern 6: API ReferenceComponentsThe built-in browser component lets you render different kinds of form inputs. Reference Usage Displaying inputs of different types Providing a label for an input Providing an initial value for an input Reading the input values when submitting a form Controlling an input with a state variable Optimizing re-rendering on every keystroke Troubleshooting My text input doesn’t update when I type into it My checkbox doesn’t update when I click on it My input caret jumps to the beginning on every keystroke I’m getting an error: “A component is changing an uncontrolled input to be controlled” Reference To display an input, render the built-in browser component. See more examples below. Props supports all common element props. formAction: A string or function. Overrides the parent for type="submit" and type="image". When a URL is passed to action the form will behave like a standard HTML form. When a function is passed to formAction the function will handle the form submission. See . You can make an input controlled by passing one of these props: checked: A boolean. For a checkbox input or a radio button, controls whether it is selected. value: A string. For a text input, controls its text. (For a radio button, specifies its form data.) When you pass either of them, you must also pass an onChange handler that updates the passed value. These props are only relevant for uncontrolled inputs: defaultChecked: A boolean. Specifies the initial value for type="checkbox" and type="radio" inputs. defaultValue: A string. Specifies the initial value for a text input. These props are relevant both for uncontrolled and controlled inputs: accept: A string. Specifies which filetypes are accepted by a type="file" input. alt: A string. Specifies the alternative image text for a type="image" input. capture: A string. Specifies the media (microphone, video, or camera) captured by a type="file" input. autoComplete: A string. Specifies one of the possible autocomplete behaviors. autoFocus: A boolean. If true, React will focus the element on mount. dirname: A string. Specifies the form field name for the element’s directionality. disabled: A boolean. If true, the input will not be interactive and will appear dimmed. children: does not accept children. form: A string. Specifies the id of the this input belongs to. If omitted, it’s the closest parent form. formAction: A string. Overrides the parent for type="submit" and type="image". formEnctype: A string. Overrides the parent for type="submit" and type="image". formMethod: A string. Overrides the parent for type="submit" and type="image". formNoValidate: A string. Overrides the parent for type="submit" and type="image". formTarget: A string. Overrides the parent for type="submit" and type="image". height: A string. Specifies the image height for type="image". list: A string. Specifies the id of the with the autocomplete options. max: A number. Specifies the maximum value of numerical and datetime inputs. maxLength: A number. Specifies the maximum length of text and other inputs. min: A number. Specifies the minimum value of numerical and datetime inputs. minLength: A number. Specifies the minimum length of text and other inputs. multiple: A boolean. Specifies whether multiple values are allowed for <type="file" and type="email". name: A string. Specifies the name for this input that’s submitted with the form. onChange: An Event handler function. Required for controlled inputs. Fires immediately when the input’s value is changed by the user (for example, it fires on every keystroke). Behaves like the browser input event. onChangeCapture: A version of onChange that fires in the capture phase. onInput: An Event handler function. Fires immediately when the value is changed by the user. For historical reasons, in React it is idiomatic to use onChange instead which works similarly. onInputCapture: A version of onInput that fires in the capture phase. onInvalid: An Event handler function. Fires if an input fails validation on form submit. Unlike the built-in invalid event, the React onInvalid event bubbles. onInvalidCapture: A version of onInvalid that fires in the capture phase. onSelect: An Event handler function. Fires after the selection inside the changes. React extends the onSelect event to also fire for empty selection and on edits (which may affect the selection). onSelectCapture: A version of onSelect that fires in the capture phase. pattern: A string. Specifies the pattern that the value must match. placeholder: A string. Displayed in a dimmed color when the input value is empty. readOnly: A boolean. If true, the input is not editable by the user. required: A boolean. If true, the value must be provided for the form to submit. size: A number. Similar to setting width, but the unit depends on the control. src: A string. Specifies the image source for a type="image" input. step: A positive number or an 'any' string. Specifies the distance between valid values. type: A string. One of the input types. width: A string. Specifies the image width for a type="image" input. Caveats Checkboxes need checked (or defaultChecked), not value (or defaultValue). If a text input receives a string value prop, it will be treated as controlled. If a checkbox or a radio button receives a boolean checked prop, it will be treated as controlled. An input can’t be both controlled and uncontrolled at the same time. An input cannot switch between being controlled or uncontrolled over its lifetime. Every controlled input needs an onChange event handler that synchronously updates its backing value. Usage Displaying inputs of different types To display an input, render an component. By default, it will be a text input. You can pass type="checkbox" for a checkbox, type="radio" for a radio button, or one of the other input types. App.jsApp.jsReloadClearForkexport default function MyForm() { return ( <> Text input: Checkbox: Radio buttons: Option 1 Option 2 Option 3 </> ); } Show more Providing a label for an input Typically, you will place every inside a tag. This tells the browser that this label is associated with that input. When the user clicks the label, the browser will automatically focus the input. It’s also essential for accessibility: a screen reader will announce the label caption when the user focuses the associated input. If you can’t nest into a , associate them by passing the same ID to and . To avoid conflicts between multiple instances of one component, generate such an ID with useId. App.jsApp.jsReloadClearForkimport { useId } from 'react'; export default function Form() { const ageInputId = useId(); return ( <> Your first name: Your age: </> ); } Show more Providing an initial value for an input You can optionally specify the initial value for any input. Pass it as the defaultValue string for text inputs. Checkboxes and radio buttons should specify the initial value with the defaultChecked boolean instead. App.jsApp.jsReloadClearForkexport default function MyForm() { return ( <> Text input: Checkbox: Radio buttons: Option 1 Option 2 Option 3 </> ); } Show more Reading the input values when submitting a form Add a around your inputs with a inside. It will call your event handler. By default, the browser will send the form data to the current URL and refresh the page. You can override that behavior by calling e.preventDefault(). Read the form data with new FormData(e.target). App.jsApp.jsReloadClearForkexport default function MyForm() { function handleSubmit(e) { // Prevent the browser from reloading the page e.preventDefault(); // Read the form data const form = e.target; const formData = new FormData(form); // You can pass formData as a fetch body directly: fetch('/some-api', { method: form.method, body: formData }); // Or you can work with it as a plain object: const formJson = Object.fromEntries(formData.entries()); console.log(formJson); } return ( Text input: Checkbox: Radio buttons: Option 1 Option 2 Option 3 Reset form Submit form ); } Show more NoteGive a name to every , for example . The name you specified will be used as a key in the form data, for example { firstName: "Taylor" }. PitfallBy default, a inside a without a type attribute will submit it. This can be surprising! If you have your own custom Button React component, consider using instead of (with no type). Then, to be explicit, use for buttons that are supposed to submit the form. Controlling an input with a state variable An input like is uncontrolled. Even if you pass an initial value like , your JSX only specifies the initial value. It does not control what the value should be right now. To render a controlled input, pass the value prop to it (or checked for checkboxes and radios). React will force the input to always have the value you passed. Usually, you would do this by declaring a state variable: function Form() { const [firstName, setFirstName] = useState(''); // Declare a state variable... // ... return ( <input value={firstName} // ...force the input's value to match the state variable... onChange={e => setFirstName(e.target.value)} // ... and update the state variable on any edits! /> );} A controlled input makes sense if you needed state anyway—for example, to re-render your UI on every edit: function Form() { const [firstName, setFirstName] = useState(''); return ( <> First name: <input value={firstName} onChange={e => setFirstName(e.target.value)} /> {firstName !== '' && Your name is {firstName}.} ... It’s also useful if you want to offer multiple ways to adjust the input state (for example, by clicking a button): function Form() { // ... const [age, setAge] = useState(''); const ageAsNumber = Number(age); return ( <> Age: <input value={age} onChange={e => setAge(e.target.value)} type="number" /> <button onClick={() => setAge(ageAsNumber + 10)}> Add 10 years The value you pass to controlled components should not be undefined or null. If you need the initial value to be empty (such as with the firstName field below), initialize your state variable to an empty string (''). App.jsApp.jsReloadClearForkimport { useState } from 'react'; export default function Form() { const [firstName, setFirstName] = useState(''); const [age, setAge] = useState('20'); const ageAsNumber = Number(age); return ( <> First name: <input value={firstName} onChange={e => setFirstName(e.target.value)} /> Age: <input value={age} onChange={e => setAge(e.target.value)} type="number" /> <button onClick={() => setAge(ageAsNumber + 10)}> Add 10 years {firstName !== '' && Your name is {firstName}. } {ageAsNumber > 0 && Your age is {ageAsNumber}. } </> ); } Show more PitfallIf you pass value without onChange, it will be impossible to type into the input. When you control an input by passing some value to it, you force it to always have the value you passed. So if you pass a state variable as a value but forget to update that state variable synchronously during the onChange event handler, React will revert the input after every keystroke back to the value that you specified. Optimizing re-rendering on every keystroke When you use a controlled input, you set the state on every keystroke. If the component containing your state re-renders a large tree, this can get slow. There’s a few ways you can optimize re-rendering performance. For example, suppose you start with a form that re-renders all page content on every keystroke: function App() { const [firstName, setFirstName] = useState(''); return ( <> <input value={firstName} onChange={e => setFirstName(e.target.value)} /> </> );} Since doesn’t rely on the input state, you can move the input state into its own component: function App() { return ( <> </> );}function SignupForm() { const [firstName, setFirstName] = useState(''); return ( <input value={firstName} onChange={e => setFirstName(e.target.value)} /> );} This significantly improves performance because now only SignupForm re-renders on every keystroke. If there is no way to avoid re-rendering (for example, if PageContent depends on the search input’s value), useDeferredValue lets you keep the controlled input responsive even in the middle of a large re-render. Troubleshooting My text input doesn’t update when I type into it If you render an input with value but no onChange, you will see an error in the console: // 🔴 Bug: controlled text input with no onChange handler ConsoleYou provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly. As the error message suggests, if you only wanted to specify the initial value, pass defaultValue instead: // ✅ Good: uncontrolled input with an initial value If you want to control this input with a state variable, specify an onChange handler: // ✅ Good: controlled input with onChange<input value={something} onChange={e => setSomething(e.target.value)} /> If the value is intentionally read-only, add a readOnly prop to suppress the error: // ✅ Good: readonly controlled input without on change My checkbox doesn’t update when I click on it If you render a checkbox with checked but no onChange, you will see an error in the console: // 🔴 Bug: controlled checkbox with no onChange handler ConsoleYou provided a checked prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultChecked. Otherwise, set either onChange or readOnly. As the error message suggests, if you only wanted to specify the initial value, pass defaultChecked instead: // ✅ Good: uncontrolled checkbox with an initial value If you want to control this checkbox with a state variable, specify an onChange handler: // ✅ Good: controlled checkbox with onChange<input type="checkbox" checked={something} onChange={e => setSomething(e.target.checked)} /> PitfallYou need to read e.target.checked rather than e.target.value for checkboxes. If the checkbox is intentionally read-only, add a readOnly prop to suppress the error: // ✅ Good: readonly controlled input without on change My input caret jumps to the beginning on every keystroke If you control an input, you must update its state variable to the input’s value from the DOM during onChange. You can’t update it to something other than e.target.value (or e.target.checked for checkboxes): function handleChange(e) { // 🔴 Bug: updating an input to something other than e.target.value setFirstName(e.target.value.toUpperCase());} You also can’t update it asynchronously: function handleChange(e) { // 🔴 Bug: updating an input asynchronously setTimeout(() => { setFirstName(e.target.value); }, 100);} To fix your code, update it synchronously to e.target.value: function handleChange(e) { // ✅ Updating a controlled input to e.target.value synchronously setFirstName(e.target.value);} If this doesn’t fix the problem, it’s possible that the input gets removed and re-added from the DOM on every keystroke. This can happen if you’re accidentally resetting state on every re-render, for example if the input or one of its parents always receives a different key attribute, or if you nest component function definitions (which is not supported and causes the “inner” component to always be considered a different tree). I’m getting an error: “A component is changing an uncontrolled input to be controlled” If you provide a value to the component, it must remain a string throughout its lifetime. You cannot pass value={undefined} first and later pass value="some string" because React won’t know whether you want the component to be uncontrolled or controlled. A controlled component should always receive a string value, not null or undefined. If your value is coming from an API or a state variable, it might be initialized to null or undefined. In that case, either set it to an empty string ('') initially, or pass value={someValue ?? ''} to ensure value is a string. Similarly, if you pass checked to a checkbox, ensure it’s always a boolean.PreviousNext
<input>
Pattern 7: API ReferenceOverviewRules of HooksHooks are defined using JavaScript functions, but they represent a special type of reusable UI logic with restrictions on where they can be called. Only call Hooks at the top level Only call Hooks from React functions Only call Hooks at the top level Functions whose names start with use are called Hooks in React. Don’t call Hooks inside loops, conditions, nested functions, or try/catch/finally blocks. Instead, always use Hooks at the top level of your React function, before any early returns. You can only call Hooks while React is rendering a function component: ✅ Call them at the top level in the body of a function component. ✅ Call them at the top level in the body of a custom Hook. function Counter() { // ✅ Good: top-level in a function component const [count, setCount] = useState(0); // ...}function useWindowWidth() { // ✅ Good: top-level in a custom Hook const [width, setWidth] = useState(window.innerWidth); // ...} It’s not supported to call Hooks (functions starting with use) in any other cases, for example: 🔴 Do not call Hooks inside conditions or loops. 🔴 Do not call Hooks after a conditional return statement. 🔴 Do not call Hooks in event handlers. 🔴 Do not call Hooks in class components. 🔴 Do not call Hooks inside functions passed to useMemo, useReducer, or useEffect. 🔴 Do not call Hooks inside try/catch/finally blocks. If you break these rules, you might see this error. function Bad({ cond }) { if (cond) { // 🔴 Bad: inside a condition (to fix, move it outside!) const theme = useContext(ThemeContext); } // ...}function Bad() { for (let i = 0; i < 10; i++) { // 🔴 Bad: inside a loop (to fix, move it outside!) const theme = useContext(ThemeContext); } // ...}function Bad({ cond }) { if (cond) { return; } // 🔴 Bad: after a conditional return (to fix, move it before the return!) const theme = useContext(ThemeContext); // ...}function Bad() { function handleClick() { // 🔴 Bad: inside an event handler (to fix, move it outside!) const theme = useContext(ThemeContext); } // ...}function Bad() { const style = useMemo(() => { // 🔴 Bad: inside useMemo (to fix, move it outside!) const theme = useContext(ThemeContext); return createStyle(theme); }); // ...}class Bad extends React.Component { render() { // 🔴 Bad: inside a class component (to fix, write a function component instead of a class!) useEffect(() => {}) // ... }}function Bad() { try { // 🔴 Bad: inside try/catch/finally block (to fix, move it outside!) const [x, setX] = useState(0); } catch { const [x, setX] = useState(1); }} You can use the eslint-plugin-react-hooks plugin to catch these mistakes. NoteCustom Hooks may call other Hooks (that’s their whole purpose). This works because custom Hooks are also supposed to only be called while a function component is rendering. Only call Hooks from React functions Don’t call Hooks from regular JavaScript functions. Instead, you can: ✅ Call Hooks from React function components. ✅ Call Hooks from custom Hooks. By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code. function FriendList() { const [onlineStatus, setOnlineStatus] = useOnlineStatus(); // ✅}function setOnlineStatus() { // ❌ Not a component or custom Hook! const [onlineStatus, setOnlineStatus] = useOnlineStatus();}PreviousReact calls Components and Hooks
use
Pattern 8: API ReferenceLegacy React APIsChildrenPitfallUsing Children is uncommon and can lead to fragile code. See common alternatives. Children lets you manipulate and transform the JSX you received as the children prop.const mappedChildren = Children.map(children, child => {child} ); Reference Children.count(children) Children.forEach(children, fn, thisArg?) Children.map(children, fn, thisArg?) Children.only(children) Children.toArray(children) Usage Transforming children Running some code for each child Counting children Converting children to an array Alternatives Exposing multiple components Accepting an array of objects as a prop Calling a render prop to customize rendering Troubleshooting I pass a custom component, but the Children methods don’t show its render result Reference Children.count(children) Call Children.count(children) to count the number of children in the children data structure. import { Children } from 'react';function RowList({ children }) { return ( <> Total rows: {Children.count(children)} ... </> );} See more examples below. Parameters children: The value of the children prop received by your component. Returns The number of nodes inside these children. Caveats Empty nodes (null, undefined, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed. Children.forEach(children, fn, thisArg?) Call Children.forEach(children, fn, thisArg?) to run some code for each child in the children data structure. import { Children } from 'react';function SeparatorList({ children }) { const result = []; Children.forEach(children, (child, index) => { result.push(child); result.push(); }); // ... See more examples below. Parameters children: The value of the children prop received by your component. fn: The function you want to run for each child, similar to the array forEach method callback. It will be called with the child as the first argument and its index as the second argument. The index starts at 0 and increments on each call. optional thisArg: The this value with which the fn function should be called. If omitted, it’s undefined. Returns Children.forEach returns undefined. Caveats Empty nodes (null, undefined, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed. Children.map(children, fn, thisArg?) Call Children.map(children, fn, thisArg?) to map or transform each child in the children data structure. import { Children } from 'react';function RowList({ children }) { return ( {Children.map(children, child => {child} )} );} See more examples below. Parameters children: The value of the children prop received by your component. fn: The mapping function, similar to the array map method callback. It will be called with the child as the first argument and its index as the second argument. The index starts at 0 and increments on each call. You need to return a React node from this function. This may be an empty node (null, undefined, or a Boolean), a string, a number, a React element, or an array of other React nodes. optional thisArg: The this value with which the fn function should be called. If omitted, it’s undefined. Returns If children is null or undefined, returns the same value. Otherwise, returns a flat array consisting of the nodes you’ve returned from the fn function. The returned array will contain all nodes you returned except for null and undefined. Caveats Empty nodes (null, undefined, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed. If you return an element or an array of elements with keys from fn, the returned elements’ keys will be automatically combined with the key of the corresponding original item from children. When you return multiple elements from fn in an array, their keys only need to be unique locally amongst each other. Children.only(children) Call Children.only(children) to assert that children represent a single React element. function Box({ children }) { const element = Children.only(children); // ... Parameters children: The value of the children prop received by your component. Returns If children is a valid element, returns that element. Otherwise, throws an error. Caveats This method always throws if you pass an array (such as the return value of Children.map) as children. In other words, it enforces that children is a single React element, not that it’s an array with a single element. Children.toArray(children) Call Children.toArray(children) to create an array out of the children data structure. import { Children } from 'react';export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); // ... Parameters children: The value of the children prop received by your component. Returns Returns a flat array of elements in children. Caveats Empty nodes (null, undefined, and Booleans) will be omitted in the returned array. The returned elements’ keys will be calculated from the original elements’ keys and their level of nesting and position. This ensures that flattening the array does not introduce changes in behavior. Usage Transforming children To transform the children JSX that your component receives as the children prop, call Children.map: import { Children } from 'react';function RowList({ children }) { return ( {Children.map(children, child => {child} )} );} In the example above, the RowList wraps every child it receives into a 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: This is the first item. This is the second item. This is the third item. 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 ( <RowList rows={[ { id: 'first', content: 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 ( <TabSwitcher tabs={[ { id: 'first', header: 'First', content: 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 ( <TabSwitcher tabIds={['first', 'second', 'third']} getHeader={tabId => { 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 ( <RowList rowIds={['first', 'second', 'third']} renderRow={(id, index) => { return ( <Row isHighlighted={index % 2 === 0}> 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
Example Code Patterns
Example 1 (jsx):
export default function Square() { return <button className="square">X</button>;}
Example 2 (jsx):
export default function Square() { return <button className="square">X</button>;}
Example 3 (python):
npm install -D babel-plugin-react-compiler@latest
Example 4 (python):
yarn add -D babel-plugin-react-compiler@latest
Example 5 (jsx):
const cachedFn = useCallback(fn, dependencies)
Reference Files
This skill includes comprehensive documentation in references/:
- api.md - Api documentation
- components.md - Components documentation
- getting_started.md - Getting Started documentation
- hooks.md - Hooks documentation
- other.md - Other documentation
- state.md - State documentation
Use view to read specific reference files when detailed information is needed.
Working with This Skill
For Beginners
Start with the getting_started or tutorials reference files for foundational concepts.
For Specific Features
Use the appropriate category reference file (api, guides, etc.) for detailed information.
For Code Examples
The quick reference section above contains common patterns extracted from the official docs.
Resources
references/
Organized documentation extracted from official sources. These files contain:
- Detailed explanations
- Code examples with language annotations
- Links to original documentation
- Table of contents for quick navigation
scripts/
Add helper scripts here for common automation tasks.
assets/
Add templates, boilerplate, or example projects here.
Notes
- This skill was automatically generated from official documentation
- Reference files preserve the structure and examples from source docs
- Code examples include language detection for better syntax highlighting
- Quick reference patterns are extracted from common usage examples in the docs
Updating
To refresh this skill with updated documentation:
- Re-run the scraper with the same configuration
- The skill will be rebuilt with the latest information
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon
