React framework for building user interfaces. Use for React components, hooks, state management, JSX, and modern frontend development.
Comprehensive assistance with react development, generated from official documentation.
This skill should be triggered when:
Pattern 1: 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: <div ref={myRef}> 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 <div>, 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 ( <> <input ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); } Show more To implement this: Declare inputRef with the useRef Hook. Pass it as <input ref={inputRef}>. This tells React to put this <input>’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 <button> 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 ( <> <nav> <button onClick={handleScrollToFirstCat}> Neo </button> <button onClick={handleScrollToSecondCat}> Millie </button> <button onClick={handleScrollToThirdCat}> Bella </button> </nav> <div> <ul> <li> <img src="https://placecats.com/neo/300/200" alt="Neo" ref={firstCatRef} /> </li> <li> <img src="https://placecats.com/millie/200/200" alt="Millie" ref={secondCatRef} /> </li> <li> <img src="https://placecats.com/bella/199/200" alt="Bella" ref={thirdCatRef} /> </li> </ul> </div> </> ); } 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:<ul> {items.map((item) => { // Doesn't work! const ref = useRef(null); return <li ref={ref} />; })}</ul>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 ( <> <nav> <button onClick={() => scrollToCat(catList[0])}>Neo</button> <button onClick={() => scrollToCat(catList[5])}>Millie</button> <button onClick={() => scrollToCat(catList[8])}>Bella</button> </nav> <div> <ul> {catList.map((cat) => ( <li key={cat.id} ref={(node) => { const map = getMap(); map.set(cat, node); return () => { map.delete(cat); }; }} > <img src={cat.imageUrl} /> </li> ))} </ul> </div> </> ); } function setupCatList() { const catCount = 10; const catList = new Array(catCount) for (let i = 0; i < catCount; i++) { let imageUrl = ''; if (i < 5) { imageUrl = ""; } else if (i < 8) { imageUrl = ""; } else { imageUrl = ""; } 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:<li key={cat.id} ref={node => { 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 <input ref={ref} />;}function MyForm() { const inputRef = useRef(null); return <MyInput ref={inputRef} />} 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 <input>. Because <input> is a built-in component React sets the .current property of the ref to the <input> DOM element. The inputRef created in MyForm now points to the <input> DOM element returned by MyInput. A click handler created in MyForm can access inputRef and call focus() to set the focus on <input>. App.jsApp.jsReloadClearForkimport { useRef } from 'react'; function MyInput({ ref }) { return <input ref={ref} />; } export default function MyForm() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <MyInput ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); } 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 <input ref={realInputRef} />; }; export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <MyInput ref={inputRef} /> <button onClick={handleClick}>Focus the input</button> </> ); } 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 ( <> <button onClick={handleAdd}> Add </button> <input value={text} onChange={e => setText(e.target.value)} /> <ul ref={listRef}> {todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> </> ); } 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 ( <> <button onClick={handleAdd}> Add </button> <input value={text} onChange={e => setText(e.target.value)} /> <ul ref={listRef}> {todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> </> ); } 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 ( <div> <button onClick={() => { setShow(!show); }}> Toggle with setState </button> <button onClick={() => { ref.current.remove(); }}> Remove from the DOM </button> {show && <p ref={ref}>Hello world</p>} </div> ); } 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 <div> 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 <div ref={myRef}>. 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 <video>. 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 ( <> <button onClick={handleClick}> {isPlaying ? 'Pause' : 'Play'} </button> <video width="250"> <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4" /> </video> </> ) } 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 2: // You can use any browser APIs, for example:myRef.current.scrollIntoView();
// You can use any browser APIs, for example:myRef.current.scrollIntoView();
Pattern 3: Learn ReactManaging StateChoosing the State StructureStructuring state well can make a difference between a component that is pleasant to modify and debug, and one that is a constant source of bugs. Here are some tips you should consider when structuring state. You will learn When to use a single vs multiple state variables What to avoid when organizing state How to fix common issues with the state structure Principles for structuring state When you write a component that holds some state, you’ll have to make choices about how many state variables to use and what the shape of their data should be. While it’s possible to write correct programs even with a suboptimal state structure, there are a few principles that can guide you to make better choices: Group related state. If you always update two or more state variables at the same time, consider merging them into a single state variable. Avoid contradictions in state. When the state is structured in a way that several pieces of state may contradict and “disagree” with each other, you leave room for mistakes. Try to avoid this. Avoid redundant state. If you can calculate some information from the component’s props or its existing state variables during rendering, you should not put that information into that component’s state. Avoid duplication in state. When the same data is duplicated between multiple state variables, or within nested objects, it is difficult to keep them in sync. Reduce duplication when you can. Avoid deeply nested state. Deeply hierarchical state is not very convenient to update. When possible, prefer to structure state in a flat way. The goal behind these principles is to make state easy to update without introducing mistakes. Removing redundant and duplicate data from state helps ensure that all its pieces stay in sync. This is similar to how a database engineer might want to “normalize” the database structure to reduce the chance of bugs. To paraphrase Albert Einstein, “Make your state as simple as it can be—but no simpler.” Now let’s see how these principles apply in action. Group related state You might sometimes be unsure between using a single or multiple state variables. Should you do this? const [x, setX] = useState(0);const [y, setY] = useState(0); Or this? const [position, setPosition] = useState({ x: 0, y: 0 }); Technically, you can use either of these approaches. But if some two state variables always change together, it might be a good idea to unify them into a single state variable. Then you won’t forget to always keep them in sync, like in this example where moving the cursor updates both coordinates of the red dot: App.jsApp.jsReloadClearForkimport { useState } from 'react'; export default function MovingDot() { const [position, setPosition] = useState({ x: 0, y: 0 }); return ( <div onPointerMove={e => { setPosition({ x: e.clientX, y: e.clientY }); }} style={{ position: 'relative', width: '100vw', height: '100vh', }}> <div style={{ position: 'absolute', backgroundColor: 'red', borderRadius: '50%', transform: translate(${position.x}px, ${position.y}px), left: -10, top: -10, width: 20, height: 20, }} /> </div> ) } Show more Another case where you’ll group data into an object or an array is when you don’t know how many pieces of state you’ll need. For example, it’s helpful when you have a form where the user can add custom fields. PitfallIf your state variable is an object, remember that you can’t update only one field in it without explicitly copying the other fields. For example, you can’t do setPosition({ x: 100 }) in the above example because it would not have the y property at all! Instead, if you wanted to set x alone, you would either do setPosition({ ...position, x: 100 }), or split them into two state variables and do setX(100). Avoid contradictions in state Here is a hotel feedback form with isSending and isSent state variables: App.jsApp.jsReloadClearForkimport { useState } from 'react'; export default function FeedbackForm() { const [text, setText] = useState(''); const [isSending, setIsSending] = useState(false); const [isSent, setIsSent] = useState(false); async function handleSubmit(e) { e.preventDefault(); setIsSending(true); await sendMessage(text); setIsSending(false); setIsSent(true); } if (isSent) { return <h1>Thanks for feedback!</h1> } return ( <form onSubmit={handleSubmit}> <p>How was your stay at The Prancing Pony?</p> <textarea disabled={isSending} value={text} onChange={e => setText(e.target.value)} /> <br /> <button disabled={isSending} type="submit" > Send </button> {isSending && <p>Sending...</p>} </form> ); } // Pretend to send a message. function sendMessage(text) { return new Promise(resolve => { setTimeout(resolve, 2000); }); } Show more While this code works, it leaves the door open for “impossible” states. For example, if you forget to call setIsSent and setIsSending together, you may end up in a situation where both isSending and isSent are true at the same time. The more complex your component is, the harder it is to understand what happened. Since isSending and isSent should never be true at the same time, it is better to replace them with one status state variable that may take one of three valid states: 'typing' (initial), 'sending', and 'sent': App.jsApp.jsReloadClearForkimport { useState } from 'react'; export default function FeedbackForm() { const [text, setText] = useState(''); const [status, setStatus] = useState('typing'); async function handleSubmit(e) { e.preventDefault(); setStatus('sending'); await sendMessage(text); setStatus('sent'); } const isSending = status === 'sending'; const isSent = status === 'sent'; if (isSent) { return <h1>Thanks for feedback!</h1> } return ( <form onSubmit={handleSubmit}> <p>How was your stay at The Prancing Pony?</p> <textarea disabled={isSending} value={text} onChange={e => setText(e.target.value)} /> <br /> <button disabled={isSending} type="submit" > Send </button> {isSending && <p>Sending...</p>} </form> ); } // Pretend to send a message. function sendMessage(text) { return new Promise(resolve => { setTimeout(resolve, 2000); }); } Show more You can still declare some constants for readability: const isSending = status === 'sending';const isSent = status === 'sent'; But they’re not state variables, so you don’t need to worry about them getting out of sync with each other. Avoid redundant state If you can calculate some information from the component’s props or its existing state variables during rendering, you should not put that information into that component’s state. For example, take this form. It works, but can you find any redundant state in it? App.jsApp.jsReloadClearForkimport { useState } from 'react'; export default function Form() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const [fullName, setFullName] = useState(''); function handleFirstNameChange(e) { setFirstName(e.target.value); setFullName(e.target.value + ' ' + lastName); } function handleLastNameChange(e) { setLastName(e.target.value); setFullName(firstName + ' ' + e.target.value); } return ( <> <h2>Let’s check you in</h2> <label> First name:{' '} <input value={firstName} onChange={handleFirstNameChange} /> </label> <label> Last name:{' '} <input value={lastName} onChange={handleLastNameChange} /> </label> <p> Your ticket will be issued to: <b>{fullName}</b> </p> </> ); } Show more This form has three state variables: firstName, lastName, and fullName. However, fullName is redundant. You can always calculate fullName from firstName and lastName during render, so remove it from state. This is how you can do it: App.jsApp.jsReloadClearForkimport { useState } from 'react'; export default function Form() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const fullName = firstName + ' ' + lastName; function handleFirstNameChange(e) { setFirstName(e.target.value); } function handleLastNameChange(e) { setLastName(e.target.value); } return ( <> <h2>Let’s check you in</h2> <label> First name:{' '} <input value={firstName} onChange={handleFirstNameChange} /> </label> <label> Last name:{' '} <input value={lastName} onChange={handleLastNameChange} /> </label> <p> Your ticket will be issued to: <b>{fullName}</b> </p> </> ); } Show more Here, fullName is not a state variable. Instead, it’s calculated during render: const fullName = firstName + ' ' + lastName; As a result, the change handlers don’t need to do anything special to update it. When you call setFirstName or setLastName, you trigger a re-render, and then the next fullName will be calculated from the fresh data. Deep DiveDon’t mirror props in state Show DetailsA common example of redundant state is code like this:function Message({ messageColor }) { const [color, setColor] = useState(messageColor);Here, a color state variable is initialized to the messageColor prop. The problem is that if the parent component passes a different value of messageColor later (for example, 'red' instead of 'blue'), the color state variable would not be updated! The state is only initialized during the first render.This is why “mirroring” some prop in a state variable can lead to confusion. Instead, use the messageColor prop directly in your code. If you want to give it a shorter name, use a constant:function Message({ messageColor }) { const color = messageColor;This way it won’t get out of sync with the prop passed from the parent component.”Mirroring” props into state only makes sense when you want to ignore all updates for a specific prop. By convention, start the prop name with initial or default to clarify that its new values are ignored:function Message({ initialColor }) { // The color state variable holds the first value of initialColor. // Further changes to the initialColor prop are ignored. const [color, setColor] = useState(initialColor); Avoid duplication in state This menu list component lets you choose a single travel snack out of several: App.jsApp.jsReloadClearForkimport { useState } from 'react'; const initialItems = [ { title: 'pretzels', id: 0 }, { title: 'crispy seaweed', id: 1 }, { title: 'granola bar', id: 2 }, ]; export default function Menu() { const [items, setItems] = useState(initialItems); const [selectedItem, setSelectedItem] = useState( items[0] ); return ( <> <h2>What's your travel snack?</h2> <ul> {items.map(item => ( <li key={item.id}> {item.title} {' '} <button onClick={() => { setSelectedItem(item); }}>Choose</button> </li> ))} </ul> <p>You picked {selectedItem.title}.</p> </> ); } Show more Currently, it stores the selected item as an object in the selectedItem state variable. However, this is not great: the contents of the selectedItem is the same object as one of the items inside the items list. This means that the information about the item itself is duplicated in two places. Why is this a problem? Let’s make each item editable: App.jsApp.jsReloadClearForkimport { useState } from 'react'; const initialItems = [ { title: 'pretzels', id: 0 }, { title: 'crispy seaweed', id: 1 }, { title: 'granola bar', id: 2 }, ]; export default function Menu() { const [items, setItems] = useState(initialItems); const [selectedItem, setSelectedItem] = useState( items[0] ); function handleItemChange(id, e) { setItems(items.map(item => { if (item.id === id) { return { ...item, title: e.target.value, }; } else { return item; } })); } return ( <> <h2>What's your travel snack?</h2> <ul> {items.map((item, index) => ( <li key={item.id}> <input value={item.title} onChange={e => { handleItemChange(item.id, e) }} /> {' '} <button onClick={() => { setSelectedItem(item); }}>Choose</button> </li> ))} </ul> <p>You picked {selectedItem.title}.</p> </> ); } Show more Notice how if you first click “Choose” on an item and then edit it, the input updates but the label at the bottom does not reflect the edits. This is because you have duplicated state, and you forgot to update selectedItem. Although you could update selectedItem too, an easier fix is to remove duplication. In this example, instead of a selectedItem object (which creates a duplication with objects inside items), you hold the selectedId in state, and then get the selectedItem by searching the items array for an item with that ID: App.jsApp.jsReloadClearForkimport { useState } from 'react'; const initialItems = [ { title: 'pretzels', id: 0 }, { title: 'crispy seaweed', id: 1 }, { title: 'granola bar', id: 2 }, ]; export default function Menu() { const [items, setItems] = useState(initialItems); const [selectedId, setSelectedId] = useState(0); const selectedItem = items.find(item => item.id === selectedId ); function handleItemChange(id, e) { setItems(items.map(item => { if (item.id === id) { return { ...item, title: e.target.value, }; } else { return item; } })); } return ( <> <h2>What's your travel snack?</h2> <ul> {items.map((item, index) => ( <li key={item.id}> <input value={item.title} onChange={e => { handleItemChange(item.id, e) }} /> {' '} <button onClick={() => { setSelectedId(item.id); }}>Choose</button> </li> ))} </ul> <p>You picked {selectedItem.title}.</p> </> ); } Show more The state used to be duplicated like this: items = [{ id: 0, title: 'pretzels'}, ...] selectedItem = {id: 0, title: 'pretzels'} But after the change it’s like this: items = [{ id: 0, title: 'pretzels'}, ...] selectedId = 0 The duplication is gone, and you only keep the essential state! Now if you edit the selected item, the message below will update immediately. This is because setItems triggers a re-render, and items.find(...) would find the item with the updated title. You didn’t need to hold the selected item in state, because only the selected ID is essential. The rest could be calculated during render. Avoid deeply nested state Imagine a travel plan consisting of planets, continents, and countries. You might be tempted to structure its state using nested objects and arrays, like in this example: App.jsplaces.jsplaces.jsReloadClearForkexport const initialTravelPlan = { id: 0, title: '(Root)', childPlaces: [{ id: 1, title: 'Earth', childPlaces: [{ id: 2, title: 'Africa', childPlaces: [{ id: 3, title: 'Botswana', childPlaces: [] }, { id: 4, title: 'Egypt', childPlaces: [] }, { id: 5, title: 'Kenya', childPlaces: [] }, { id: 6, title: 'Madagascar', childPlaces: [] }, { id: 7, title: 'Morocco', childPlaces: [] }, { id: 8, title: 'Nigeria', childPlaces: [] }, { id: 9, title: 'South Africa', childPlaces: [] }] }, { id: 10, title: 'Americas', childPlaces: [{ id: 11, title: 'Argentina', childPlaces: [] }, { id: 12, title: 'Brazil', childPlaces: [] }, { id: 13, title: 'Barbados', childPlaces: [] }, { id: 14, title: 'Canada', childPlaces: [] }, { id: 15, title: 'Jamaica', childPlaces: [] }, { id: 16, title: 'Mexico', childPlaces: [] }, { id: 17, title: 'Trinidad and Tobago', childPlaces: [] }, { id: 18, title: 'Venezuela', childPlaces: [] }] }, { id: 19, title: 'Asia', childPlaces: [{ id: 20, title: 'China', childPlaces: [] }, { id: 21, title: 'India', childPlaces: [] }, { id: 22, title: 'Singapore', childPlaces: [] }, { id: 23, title: 'South Korea', childPlaces: [] }, { id: 24, title: 'Thailand', childPlaces: [] }, { id: 25, title: 'Vietnam', childPlaces: [] }] }, { id: 26, title: 'Europe', childPlaces: [{ id: 27, title: 'Croatia', childPlaces: [], }, { id: 28, title: 'France', childPlaces: [], }, { id: 29, title: 'Germany', childPlaces: [], }, { id: 30, title: 'Italy', childPlaces: [], }, { id: 31, title: 'Portugal', childPlaces: [], }, { id: 32, title: 'Spain', childPlaces: [], }, { id: 33, title: 'Turkey', childPlaces: [], }] }, { id: 34, title: 'Oceania', childPlaces: [{ id: 35, title: 'Australia', childPlaces: [], }, { id: 36, title: 'Bora Bora (French Polynesia)', childPlaces: [], }, { id: 37, title: 'Easter Island (Chile)', childPlaces: [], }, { id: 38, title: 'Fiji', childPlaces: [], }, { id: 39, title: 'Hawaii (the USA)', childPlaces: [], }, { id: 40, title: 'New Zealand', childPlaces: [], }, { id: 41, title: 'Vanuatu', childPlaces: [], }] }] }, { id: 42, title: 'Moon', childPlaces: [{ id: 43, title: 'Rheita', childPlaces: [] }, { id: 44, title: 'Piccolomini', childPlaces: [] }, { id: 45, title: 'Tycho', childPlaces: [] }] }, { id: 46, title: 'Mars', childPlaces: [{ id: 47, title: 'Corn Town', childPlaces: [] }, { id: 48, title: 'Green Hill', childPlaces: [] }] }] }; Show more Now let’s say you want to add a button to delete a place you’ve already visited. How would you go about it? Updating nested state involves making copies of objects all the way up from the part that changed. Deleting a deeply nested place would involve copying its entire parent place chain. Such code can be very verbose. If the state is too nested to update easily, consider making it “flat”. Here is one way you can restructure this data. Instead of a tree-like structure where each place has an array of its child places, you can have each place hold an array of its child place IDs. Then store a mapping from each place ID to the corresponding place. This data restructuring might remind you of seeing a database table: App.jsplaces.jsplaces.jsReloadClearForkexport const initialTravelPlan = { 0: { id: 0, title: '(Root)', childIds: [1, 42, 46], }, 1: { id: 1, title: 'Earth', childIds: [2, 10, 19, 26, 34] }, 2: { id: 2, title: 'Africa', childIds: [3, 4, 5, 6 , 7, 8, 9] }, 3: { id: 3, title: 'Botswana', childIds: [] }, 4: { id: 4, title: 'Egypt', childIds: [] }, 5: { id: 5, title: 'Kenya', childIds: [] }, 6: { id: 6, title: 'Madagascar', childIds: [] }, 7: { id: 7, title: 'Morocco', childIds: [] }, 8: { id: 8, title: 'Nigeria', childIds: [] }, 9: { id: 9, title: 'South Africa', childIds: [] }, 10: { id: 10, title: 'Americas', childIds: [11, 12, 13, 14, 15, 16, 17, 18], }, 11: { id: 11, title: 'Argentina', childIds: [] }, 12: { id: 12, title: 'Brazil', childIds: [] }, 13: { id: 13, title: 'Barbados', childIds: [] }, 14: { id: 14, title: 'Canada', childIds: [] }, 15: { id: 15, title: 'Jamaica', childIds: [] }, 16: { id: 16, title: 'Mexico', childIds: [] }, 17: { id: 17, title: 'Trinidad and Tobago', childIds: [] }, 18: { id: 18, title: 'Venezuela', childIds: [] }, 19: { id: 19, title: 'Asia', childIds: [20, 21, 22, 23, 24, 25], }, 20: { id: 20, title: 'China', childIds: [] }, 21: { id: 21, title: 'India', childIds: [] }, 22: { id: 22, title: 'Singapore', childIds: [] }, 23: { id: 23, title: 'South Korea', childIds: [] }, 24: { id: 24, title: 'Thailand', childIds: [] }, 25: { id: 25, title: 'Vietnam', childIds: [] }, 26: { id: 26, title: 'Europe', childIds: [27, 28, 29, 30, 31, 32, 33], }, 27: { id: 27, title: 'Croatia', childIds: [] }, 28: { id: 28, title: 'France', childIds: [] }, 29: { id: 29, title: 'Germany', childIds: [] }, 30: { id: 30, title: 'Italy', childIds: [] }, 31: { id: 31, title: 'Portugal', childIds: [] }, 32: { id: 32, title: 'Spain', childIds: [] }, 33: { id: 33, title: 'Turkey', childIds: [] }, 34: { id: 34, title: 'Oceania', childIds: [35, 36, 37, 38, 39, 40, 41], }, 35: { id: 35, title: 'Australia', childIds: [] }, 36: { id: 36, title: 'Bora Bora (French Polynesia)', childIds: [] }, 37: { id: 37, title: 'Easter Island (Chile)', childIds: [] }, 38: { id: 38, title: 'Fiji', childIds: [] }, 39: { id: 40, title: 'Hawaii (the USA)', childIds: [] }, 40: { id: 40, title: 'New Zealand', childIds: [] }, 41: { id: 41, title: 'Vanuatu', childIds: [] }, 42: { id: 42, title: 'Moon', childIds: [43, 44, 45] }, 43: { id: 43, title: 'Rheita', childIds: [] }, 44: { id: 44, title: 'Piccolomini', childIds: [] }, 45: { id: 45, title: 'Tycho', childIds: [] }, 46: { id: 46, title: 'Mars', childIds: [47, 48] }, 47: { id: 47, title: 'Corn Town', childIds: [] }, 48: { id: 48, title: 'Green Hill', childIds: [] } }; Show more Now that the state is “flat” (also known as “normalized”), updating nested items becomes easier. In order to remove a place now, you only need to update two levels of state: The updated version of its parent place should exclude the removed ID from its childIds array. The updated version of the root “table” object should include the updated version of the parent place. Here is an example of how you could go about it: App.jsplaces.jsApp.jsReloadClearForkimport { useState } from 'react'; import { initialTravelPlan } from './places.js'; export default function TravelPlan() { const [plan, setPlan] = useState(initialTravelPlan); function handleComplete(parentId, childId) { const parent = plan[parentId]; // Create a new version of the parent place // that doesn't include this child ID. const nextParent = { ...parent, childIds: parent.childIds .filter(id => id !== childId) }; // Update the root state object... setPlan({ ...plan, // ...so that it has the updated parent. [parentId]: nextParent }); } const root = plan[0]; const planetIds = root.childIds; return ( <> <h2>Places to visit</h2> <ol> {planetIds.map(id => ( <PlaceTree key={id} id={id} parentId={0} placesById={plan} onComplete={handleComplete} /> ))} </ol> </> ); } function PlaceTree({ id, parentId, placesById, onComplete }) { const place = placesById[id]; const childIds = place.childIds; return ( <li> {place.title} <button onClick={() => { onComplete(parentId, id); }}> Complete </button> {childIds.length > 0 && <ol> {childIds.map(childId => ( <PlaceTree key={childId} id={childId} parentId={id} placesById={placesById} onComplete={onComplete} /> ))} </ol> } </li> ); } Show more You can nest state as much as you like, but making it “flat” can solve numerous problems. It makes state easier to update, and it helps ensure you don’t have duplication in different parts of a nested object. Deep DiveImproving memory usage Show DetailsIdeally, you would also remove the deleted items (and their children!) from the “table” object to improve memory usage. This version does that. It also uses Immer to make the update logic more concise.package.jsonApp.jsplaces.jspackage.jsonReloadClearFork{ "dependencies": { "immer": "1.7.3", "react": "latest", "react-dom": "latest", "react-scripts": "latest", "use-immer": "0.5.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }, "devDependencies": {} } Sometimes, you can also reduce state nesting by moving some of the nested state into the child components. This works well for ephemeral UI state that doesn’t need to be stored, like whether an item is hovered. Recap If two state variables always update together, consider merging them into one. Choose your state variables carefully to avoid creating “impossible” states. Structure your state in a way that reduces the chances that you’ll make a mistake updating it. Avoid redundant and duplicate state so that you don’t need to keep it in sync. Don’t put props into state unless you specifically want to prevent updates. For UI patterns like selection, keep ID or index in state instead of the object itself. If updating deeply nested state is complicated, try flattening it. Try out some challenges1. Fix a component that’s not updating 2. Fix a broken packing list 3. Fix the disappearing selection 4. Implement multiple selection Challenge 1 of 4: Fix a component that’s not updating This Clock component receives two props: color and time. When you select a different color in the select box, the Clock component receives a different color prop from its parent component. However, for some reason, the displayed color doesn’t update. Why? Fix the problem.Clock.jsClock.jsReloadClearForkimport { useState } from 'react'; export default function Clock(props) { const [color, setColor] = useState(props.color); return ( <h1 style={{ color: color }}> {props.time} </h1> ); } Show solutionNext ChallengePreviousReacting to Input with StateNextSharing State Between Components
const [x, setX] = useState(0);const [y, setY] = useState(0);
Pattern 4: The state used to be duplicated like this: items = [{ id: 0, title: 'pretzels'}, ...] selectedItem = {id: 0, title: 'pretzels'} But after the change it’s like this: items = [{ id: 0, title: 'pretzels'}, ...] selectedId = 0 The duplication is gone, and you only keep the essential state! Now if you edit the selected item, the message below will update immediately. This is because setItems triggers a re-render, and items.find(...) would find the item with the updated title. You didn’t need to hold the selected item in state, because only the selected ID is essential. The rest could be calculated during render. Avoid deeply nested state Imagine a travel plan consisting of planets, continents, and countries. You might be tempted to structure its state using nested objects and arrays, like in this example:
items = [{ id: 0, title: 'pretzels'}, ...]
Pattern 5: Learn ReactSetupUsing TypeScriptTypeScript is a popular way to add type definitions to JavaScript codebases. Out of the box, TypeScript supports JSX and you can get full React Web support by adding @types/react and @types/react-dom to your project. You will learn TypeScript with React Components Examples of typing with Hooks Common types from @types/react Further learning locations Installation All production-grade React frameworks offer support for using TypeScript. Follow the framework specific guide for installation: Next.js Remix Gatsby Expo Adding TypeScript to an existing React project To install the latest version of React’s type definitions: Terminal Copynpm install --save-dev @types/react @types/react-dom The following compiler options need to be set in your tsconfig.json: dom must be included in lib (Note: If no lib option is specified, dom is included by default). jsx must be set to one of the valid options. preserve should suffice for most applications. If you’re publishing a library, consult the jsx documentation on what value to choose. TypeScript with React Components NoteEvery file containing JSX must use the .tsx file extension. This is a TypeScript-specific extension that tells TypeScript that this file contains JSX. Writing TypeScript with React is very similar to writing JavaScript with React. The key difference when working with a component is that you can provide types for your component’s props. These types can be used for correctness checking and providing inline documentation in editors. Taking the MyButton component from the Quick Start guide, we can add a type describing the title for the button: App.tsxApp.tsxReloadClearForkTypeScript Playgroundfunction MyButton({ title }: { title: string }) { return ( <button>{title}</button> ); } export default function MyApp() { return ( <div> <h1>Welcome to my app</h1> <MyButton title="I'm a button" /> </div> ); } NoteThese sandboxes can handle TypeScript code, but they do not run the type-checker. This means you can amend the TypeScript sandboxes to learn, but you won’t get any type errors or warnings. To get type-checking, you can use the TypeScript Playground or use a more fully-featured online sandbox. This inline syntax is the simplest way to provide types for a component, though once you start to have a few fields to describe it can become unwieldy. Instead, you can use an interface or type to describe the component’s props: App.tsxApp.tsxReloadClearForkTypeScript Playgroundinterface MyButtonProps { /** The text to display inside the button / title: string; /* Whether the button can be interacted with */ disabled: boolean; } function MyButton({ title, disabled }: MyButtonProps) { return ( <button disabled={disabled}>{title}</button> ); } export default function MyApp() { return ( <div> <h1>Welcome to my app</h1> <MyButton title="I'm a disabled button" disabled={true}/> </div> ); } Show more The type describing your component’s props can be as simple or as complex as you need, though they should be an object type described with either a type or interface. You can learn about how TypeScript describes objects in Object Types but you may also be interested in using Union Types to describe a prop that can be one of a few different types and the Creating Types from Types guide for more advanced use cases. Example Hooks The type definitions from @types/react include types for the built-in Hooks, so you can use them in your components without any additional setup. They are built to take into account the code you write in your component, so you will get inferred types a lot of the time and ideally do not need to handle the minutiae of providing the types. However, we can look at a few examples of how to provide types for Hooks. useState The useState Hook will re-use the value passed in as the initial state to determine what the type of the value should be. For example: // Infer the type as "boolean"const [enabled, setEnabled] = useState(false); This will assign the type of boolean to enabled, and setEnabled will be a function accepting either a boolean argument, or a function that returns a boolean. If you want to explicitly provide a type for the state, you can do so by providing a type argument to the useState call: // Explicitly set the type to "boolean"const [enabled, setEnabled] = useState<boolean>(false); This isn’t very useful in this case, but a common case where you may want to provide a type is when you have a union type. For example, status here can be one of a few different strings: type Status = "idle" | "loading" | "success" | "error";const [status, setStatus] = useState<Status>("idle"); Or, as recommended in Principles for structuring state, you can group related state as an object and describe the different possibilities via object types: type RequestState = | { status: 'idle' } | { status: 'loading' } | { status: 'success', data: any } | { status: 'error', error: Error };const [requestState, setRequestState] = useState<RequestState>({ status: 'idle' }); useReducer The useReducer Hook is a more complex Hook that takes a reducer function and an initial state. The types for the reducer function are inferred from the initial state. You can optionally provide a type argument to the useReducer call to provide a type for the state, but it is often better to set the type on the initial state instead: App.tsxApp.tsxReloadClearForkTypeScript Playgroundimport {useReducer} from 'react'; interface State { count: number }; type CounterAction = | { type: "reset" } | { type: "setCount"; value: State["count"] } const initialState: State = { count: 0 }; function stateReducer(state: State, action: CounterAction): State { switch (action.type) { case "reset": return initialState; case "setCount": return { ...state, count: action.value }; default: throw new Error("Unknown action"); } } export default function App() { const [state, dispatch] = useReducer(stateReducer, initialState); const addFive = () => dispatch({ type: "setCount", value: state.count + 5 }); const reset = () => dispatch({ type: "reset" }); return ( <div> <h1>Welcome to my counter</h1> <p>Count: {state.count}</p> <button onClick={addFive}>Add 5</button> <button onClick={reset}>Reset</button> </div> ); } Show more We are using TypeScript in a few key places: interface State describes the shape of the reducer’s state. type CounterAction describes the different actions which can be dispatched to the reducer. const initialState: State provides a type for the initial state, and also the type which is used by useReducer by default. stateReducer(state: State, action: CounterAction): State sets the types for the reducer function’s arguments and return value. A more explicit alternative to setting the type on initialState is to provide a type argument to useReducer: import { stateReducer, State } from './your-reducer-implementation';const initialState = { count: 0 };export default function App() { const [state, dispatch] = useReducer<State>(stateReducer, initialState);} useContext The useContext Hook is a technique for passing data down the component tree without having to pass props through components. It is used by creating a provider component and often by creating a Hook to consume the value in a child component. The type of the value provided by the context is inferred from the value passed to the createContext call: App.tsxApp.tsxReloadClearForkTypeScript Playgroundimport { createContext, useContext, useState } from 'react'; type Theme = "light" | "dark" | "system"; const ThemeContext = createContext<Theme>("system"); const useGetTheme = () => useContext(ThemeContext); export default function MyApp() { const [theme, setTheme] = useState<Theme>('light'); return ( <ThemeContext value={theme}> <MyComponent /> </ThemeContext> ) } function MyComponent() { const theme = useGetTheme(); return ( <div> <p>Current theme: {theme}</p> </div> ) } Show more This technique works when you have a default value which makes sense - but there are occasionally cases when you do not, and in those cases null can feel reasonable as a default value. However, to allow the type-system to understand your code, you need to explicitly set ContextShape | null on the createContext. This causes the issue that you need to eliminate the | null in the type for context consumers. Our recommendation is to have the Hook do a runtime check for it’s existence and throw an error when not present: import { createContext, useContext, useState, useMemo } from 'react';// This is a simpler example, but you can imagine a more complex object heretype ComplexObject = { kind: string};// The context is created with | null in the type, to accurately reflect the default value.const Context = createContext<ComplexObject | null>(null);// The | null will be removed via the check in the Hook.const useGetComplexObject = () => { const object = useContext(Context); if (!object) { throw new Error("useGetComplexObject must be used within a Provider") } return object;}export default function MyApp() { const object = useMemo(() => ({ kind: "complex" }), []); return ( <Context value={object}> <MyComponent /> </Context> )}function MyComponent() { const object = useGetComplexObject(); return ( <div> <p>Current object: {object.kind}</p> </div> )} useMemo NoteReact Compiler automatically memoizes values and functions, reducing the need for manual useMemo calls. You can use the compiler to handle memoization automatically. The useMemo Hooks will create/re-access a memorized value from a function call, re-running the function only when dependencies passed as the 2nd parameter are changed. The result of calling the Hook is inferred from the return value from the function in the first parameter. You can be more explicit by providing a type argument to the Hook. // The type of visibleTodos is inferred from the return value of filterTodosconst visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); useCallback NoteReact Compiler automatically memoizes values and functions, reducing the need for manual useCallback calls. You can use the compiler to handle memoization automatically. The useCallback provide a stable reference to a function as long as the dependencies passed into the second parameter are the same. Like useMemo, the function’s type is inferred from the return value of the function in the first parameter, and you can be more explicit by providing a type argument to the Hook. const handleClick = useCallback(() => { // ...}, [todos]); When working in TypeScript strict mode useCallback requires adding types for the parameters in your callback. This is because the type of the callback is inferred from the return value of the function, and without parameters the type cannot be fully understood. Depending on your code-style preferences, you could use the *EventHandler functions from the React types to provide the type for the event handler at the same time as defining the callback: import { useState, useCallback } from 'react';export default function Form() { const [value, setValue] = useState("Change me"); const handleChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => { setValue(event.currentTarget.value); }, [setValue]) return ( <> <input value={value} onChange={handleChange} /> <p>Value: {value}</p> </> );} Useful Types There is quite an expansive set of types which come from the @types/react package, it is worth a read when you feel comfortable with how React and TypeScript interact. You can find them in React’s folder in DefinitelyTyped. We will cover a few of the more common types here. DOM Events When working with DOM events in React, the type of the event can often be inferred from the event handler. However, when you want to extract a function to be passed to an event handler, you will need to explicitly set the type of the event. App.tsxApp.tsxReloadClearForkTypeScript Playgroundimport { useState } from 'react'; export default function Form() { const [value, setValue] = useState("Change me"); function handleChange(event: React.ChangeEvent<HTMLInputElement>) { setValue(event.currentTarget.value); } return ( <> <input value={value} onChange={handleChange} /> <p>Value: {value}</p> </> ); } Show more There are many types of events provided in the React types - the full list can be found here which is based on the most popular events from the DOM. When determining the type you are looking for you can first look at the hover information for the event handler you are using, which will show the type of the event. If you need to use an event that is not included in this list, you can use the React.SyntheticEvent type, which is the base type for all events. Children There are two common paths to describing the children of a component. The first is to use the React.ReactNode type, which is a union of all the possible types that can be passed as children in JSX: interface ModalRendererProps { title: string; children: React.ReactNode;} This is a very broad definition of children. The second is to use the React.ReactElement type, which is only JSX elements and not JavaScript primitives like strings or numbers: interface ModalRendererProps { title: string; children: React.ReactElement;} Note, that you cannot use TypeScript to describe that the children are a certain type of JSX elements, so you cannot use the type-system to describe a component which only accepts <li> children. You can see an example of both React.ReactNode and React.ReactElement with the type-checker in this TypeScript playground. Style Props When using inline styles in React, you can use React.CSSProperties to describe the object passed to the style prop. This type is a union of all the possible CSS properties, and is a good way to ensure you are passing valid CSS properties to the style prop, and to get auto-complete in your editor. interface MyComponentProps { style: React.CSSProperties;} Further learning This guide has covered the basics of using TypeScript with React, but there is a lot more to learn. Individual API pages on the docs may contain more in-depth documentation on how to use them with TypeScript. We recommend the following resources: The TypeScript handbook is the official documentation for TypeScript, and covers most key language features. The TypeScript release notes cover new features in depth. React TypeScript Cheatsheet is a community-maintained cheatsheet for using TypeScript with React, covering a lot of useful edge cases and providing more breadth than this document. TypeScript Community Discord is a great place to ask questions and get help with TypeScript and React issues. PreviousEditor SetupNextReact Developer Tools
@types/react
Pattern 6: The type describing your component’s props can be as simple or as complex as you need, though they should be an object type described with either a type or interface. You can learn about how TypeScript describes objects in Object Types but you may also be interested in using Union Types to describe a prop that can be one of a few different types and the Creating Types from Types guide for more advanced use cases. Example Hooks The type definitions from @types/react include types for the built-in Hooks, so you can use them in your components without any additional setup. They are built to take into account the code you write in your component, so you will get inferred types a lot of the time and ideally do not need to handle the minutiae of providing the types. However, we can look at a few examples of how to provide types for Hooks. useState The useState Hook will re-use the value passed in as the initial state to determine what the type of the value should be. For example: // Infer the type as "boolean"const [enabled, setEnabled] = useState(false); This will assign the type of boolean to enabled, and setEnabled will be a function accepting either a boolean argument, or a function that returns a boolean. If you want to explicitly provide a type for the state, you can do so by providing a type argument to the useState call: // Explicitly set the type to "boolean"const [enabled, setEnabled] = useState<boolean>(false); This isn’t very useful in this case, but a common case where you may want to provide a type is when you have a union type. For example, status here can be one of a few different strings: type Status = "idle" | "loading" | "success" | "error";const [status, setStatus] = useState<Status>("idle"); Or, as recommended in Principles for structuring state, you can group related state as an object and describe the different possibilities via object types: type RequestState = | { status: 'idle' } | { status: 'loading' } | { status: 'success', data: any } | { status: 'error', error: Error };const [requestState, setRequestState] = useState<RequestState>({ status: 'idle' }); useReducer The useReducer Hook is a more complex Hook that takes a reducer function and an initial state. The types for the reducer function are inferred from the initial state. You can optionally provide a type argument to the useReducer call to provide a type for the state, but it is often better to set the type on the initial state instead:
type
Pattern 7: The useState Hook will re-use the value passed in as the initial state to determine what the type of the value should be. For example:
useState
Pattern 8: Learn ReactInstallationAdd React to an Existing ProjectIf you want to add some interactivity to your existing project, you don’t have to rewrite it in React. Add React to your existing stack, and render interactive React components anywhere. NoteYou need to install Node.js for local development. Although you can try React online or with a simple HTML page, realistically most JavaScript tooling you’ll want to use for development requires Node.js. Using React for an entire subroute of your existing website Let’s say you have an existing web app at example.com built with another server technology (like Rails), and you want to implement all routes starting with example.com/some-app/ fully with React. Here’s how we recommend to set it up: Build the React part of your app using one of the React-based frameworks. Specify /some-app as the base path in your framework’s configuration (here’s how: Next.js, Gatsby). Configure your server or a proxy so that all requests under /some-app/ are handled by your React app. This ensures the React part of your app can benefit from the best practices baked into those frameworks. Many React-based frameworks are full-stack and let your React app take advantage of the server. However, you can use the same approach even if you can’t or don’t want to run JavaScript on the server. In that case, serve the HTML/CSS/JS export (next export output for Next.js, default for Gatsby) at /some-app/ instead. Using React for a part of your existing page Let’s say you have an existing page built with another technology (either a server one like Rails, or a client one like Backbone), and you want to render interactive React components somewhere on that page. That’s a common way to integrate React—in fact, it’s how most React usage looked at Meta for many years! You can do this in two steps: Set up a JavaScript environment that lets you use the JSX syntax, split your code into modules with the import / export syntax, and use packages (for example, React) from the npm package registry. Render your React components where you want to see them on the page. The exact approach depends on your existing page setup, so let’s walk through some details. Step 1: Set up a modular JavaScript environment A modular JavaScript environment lets you write your React components in individual files, as opposed to writing all of your code in a single file. It also lets you use all the wonderful packages published by other developers on the npm registry—including React itself! How you do this depends on your existing setup: If your app is already split into files that use import statements, try to use the setup you already have. Check whether writing <div /> in your JS code causes a syntax error. If it causes a syntax error, you might need to transform your JavaScript code with Babel, and enable the Babel React preset to use JSX. If your app doesn’t have an existing setup for compiling JavaScript modules, set it up with Vite. The Vite community maintains many integrations with backend frameworks, including Rails, Django, and Laravel. If your backend framework is not listed, follow this guide to manually integrate Vite builds with your backend. To check whether your setup works, run this command in your project folder: Terminal Copynpm install react react-dom Then add these lines of code at the top of your main JavaScript file (it might be called index.js or main.js): index.jsindex.jsReloadClearForkimport { createRoot } from 'react-dom/client'; // Clear the existing HTML content document.body.innerHTML = '<div id="app"></div>'; // Render your React component instead const root = createRoot(document.getElementById('app')); root.render(<h1>Hello, world</h1>); If the entire content of your page was replaced by a “Hello, world!”, everything worked! Keep reading. NoteIntegrating a modular JavaScript environment into an existing project for the first time can feel intimidating, but it’s worth it! If you get stuck, try our community resources or the Vite Chat. Step 2: Render React components anywhere on the page In the previous step, you put this code at the top of your main file: import { createRoot } from 'react-dom/client';// Clear the existing HTML contentdocument.body.innerHTML = '<div id="app"></div>';// Render your React component insteadconst root = createRoot(document.getElementById('app'));root.render(<h1>Hello, world</h1>); Of course, you don’t actually want to clear the existing HTML content! Delete this code. Instead, you probably want to render your React components in specific places in your HTML. Open your HTML page (or the server templates that generate it) and add a unique id attribute to any tag, for example: <!-- ... somewhere in your html ... --><nav id="navigation"></nav><!-- ... more html ... --> This lets you find that HTML element with document.getElementById and pass it to createRoot so that you can render your own React component inside: index.jsindex.htmlindex.jsReloadClearForkimport { createRoot } from 'react-dom/client'; function NavigationBar() { // TODO: Actually implement a navigation bar return <h1>Hello from React!</h1>; } const domNode = document.getElementById('navigation'); const root = createRoot(domNode); root.render(<NavigationBar />); Notice how the original HTML content from index.html is preserved, but your own NavigationBar React component now appears inside the <nav id="navigation"> from your HTML. Read the createRoot usage documentation to learn more about rendering React components inside an existing HTML page. When you adopt React in an existing project, it’s common to start with small interactive components (like buttons), and then gradually keep “moving upwards” until eventually your entire page is built with React. If you ever reach that point, we recommend migrating to a React framework right after to get the most out of React. Using React Native in an existing native mobile app React Native can also be integrated into existing native apps incrementally. If you have an existing native app for Android (Java or Kotlin) or iOS (Objective-C or Swift), follow this guide to add a React Native screen to it.PreviousBuild a React App from ScratchNextSetup
example.com
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 (sql):
import { useRef } from 'react';
This skill includes comprehensive documentation in references/:
Use view to read specific reference files when detailed information is needed.
Start with the getting_started or tutorials reference files for foundational concepts.
Use the appropriate category reference file (api, guides, etc.) for detailed information.
The quick reference section above contains common patterns extracted from the official docs.
Organized documentation extracted from official sources. These files contain:
Add helper scripts here for common automation tasks.
Add templates, boilerplate, or example projects here.
To refresh this skill with updated documentation: