Top React Cheat Sheet Quick Reference: Ultimate Guide for Developers 2025
Our quick reference cheatsheet is the best resource to use to boost productivity and speed up the development process. Ideal for both novices and veteran developers too!
react@19
React Overview
React is an JavaScript library that can be used to construct user interfaces, specifically designed for applications that only have one page. It lets you create reuseable UI components, effective state management, as well as a declarative method of designing UI.
React Hooks
useActionState
// Importing necessary dependencies
import { useActionState } from 'react'; // Custom hook to manage action state
import { action } from './actions.js'; // Importing an external action function
// Example 1: Using useActionState in a component
function MyComponent() {
// useActionState returns the current state, formAction URL, and a pending state
const [state, formAction] = useActionState(action, null);
// state: Current state of the action
// formAction: URL to which the form will send the data
return (
<form action={formAction}>
{/* Form's action points to the generated formAction */}
{/* Additional form fields and elements go here */}
</form>
);
}
// Example 2: Defining and using an increment action
import { useActionState } from "react"; // Importing the custom hook
// Increment function to calculate the next state based on the previous state and form data
async function increment(previousState, formData) {
return previousState + 1; // Simple logic to increment the state
}
// Component that demonstrates a stateful form
function StatefulForm({}) {
// useActionState initializes the state to 0 and sets the increment function as the action
const [state, formAction] = useActionState(increment, 0);
return (
<form>
{/* Displaying the current state */}
{state}
{/* Button to trigger the increment action */}
<button formAction={formAction}>Increment</button>
</form>
);
}
useCallback
import React, { useState, useCallback } from "react";
function UserGreeting({ userName, onGreet }) {
return (
<div>
<p>Hello, {userName}!</p>
<button onClick={onGreet}>Greet</button>
</div>
);
}
function App() {
const [userName, setUserName] = useState("Alice");
// Memoize the greeting callback
const handleGreet = useCallback(() => {
alert(`Hello, ${userName}!`);
}, [userName]);
return (
<div>
<UserGreeting userName={userName} onGreet={handleGreet} />
<input
type="text"
value={userName}
onChange={(e) => setUserName(e.target.value)}
placeholder="Enter your name"
/>
</div>
);
}
export default App;
useContext
import React, { useContext } from "react";
import { ThemeProvider, ThemeContext } from "./ThemeContext";
function ThemeSwitcher() {
// Access the context value using useContext
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{ background: theme === "light" ? "#fff" : "#333", color: theme === "light" ? "#000" : "#fff" }}>
<p>Current Theme: {theme}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
export default function App() {
return (
<ThemeProvider>
<ThemeSwitcher />
</ThemeProvider>
);
}
useDebugValue
import { useState, useDebugValue } from "react";
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(false);
// Simulate a subscription (mocking the online/offline status)
React.useEffect(() => {
const interval = setInterval(() => {
setIsOnline((prevStatus) => !prevStatus); // Toggle online/offline
}, 3000);
return () => clearInterval(interval);
}, []);
// Add debug information for React DevTools
useDebugValue(isOnline ? "Online" : "Offline");
return isOnline;
}
useDeferredValue
import React, { useState, useDeferredValue } from "react";
function List({ items }) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
export default function App() {
const [search, setSearch] = useState("");
const [items, setItems] = useState(
Array.from({ length: 20000 }, (_, i) => `Item ${i + 1}`)
);
const deferredSearch = useDeferredValue(search); // Defer the search value
// Filter items based on the deferred value
const filteredItems = items.filter((item) =>
item.toLowerCase().includes(deferredSearch.toLowerCase())
);
return (
<div>
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search items..."
/>
<List items={filteredItems} />
</div>
);
}
useEffect
import React, { useState, useEffect } from "react";
function DocumentTitleUpdater() {
const [count, setCount] = useState(0);
// useEffect runs after the component renders or when 'count' changes
useEffect(() => {
// Update the document title with the current count
document.title = `Count: ${count}`;
}, [count]); // Dependency array ensures this runs only when 'count' changes
return (
<div>
<p>Count: {count}</p>
{/* Increment the count when the button is clicked */}
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default DocumentTitleUpdater;
useId
import React, { useId } from "react";
function Form() {
// Generate unique IDs for form elements
const labelId = useId();
const inputId = useId();
return (
<form>
{/* Use the generated unique IDs for accessibility */}
<label htmlFor={inputId}>Name</label>
<input id={inputId} type="text" />
</form>
);
}
export default Form;
useImperativeHandle
import React, { useRef, useImperativeHandle, forwardRef } from "react";
// Forward ref to allow parent to access child methods
const Child = forwardRef((props, ref) => {
const inputRef = useRef(null);
// Use useImperativeHandle to expose methods to the parent
useImperativeHandle(ref, () => ({
focusInput: () => {
// Focus the input element when called by the parent
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
});
function Parent() {
const childRef = useRef(null);
const handleFocus = () => {
// Call the method exposed by the child to focus the input
childRef.current.focusInput();
};
return (
<div>
<Child ref={childRef} />
<button onClick={handleFocus}>Focus the Input</button>
</div>
);
}
export default Parent;
useInsertionEffect
import React, { useState, useInsertionEffect } from "react";
function DynamicStyles() {
const [color, setColor] = useState("red");
// Inject a style dynamically before the render
useInsertionEffect(() => {
// Add dynamic style to the document head
const styleSheet = document.styleSheets[0];
styleSheet.insertRule(`.dynamicColor { color: ${color}; }`, styleSheet.cssRules.length);
}, [color]); // Re-run the effect when 'color' changes
return (
<div>
<p className="dynamicColor">This text changes color dynamically!</p>
<button onClick={() => setColor(color === "red" ? "blue" : "red")}>
Toggle Color
</button>
</div>
);
}
export default DynamicStyles;
useLayoutEffect
import React, { useState, useLayoutEffect, useRef } from "react";
function MeasureHeight() {
const [height, setHeight] = useState(0);
const divRef = useRef(null);
// useLayoutEffect runs after the DOM is updated but before the paint
useLayoutEffect(() => {
// Measure the height of the div element
if (divRef.current) {
setHeight(divRef.current.offsetHeight);
}
}, []); // Empty dependency array: runs only once after the component mounts
return (
<div>
<div ref={divRef} style={{ height: "200px", backgroundColor: "lightblue" }}>
This is a div element.
</div>
<p>Height of the div: {height}px</p>
</div>
);
}
export default MeasureHeight;
useMemo
import React, { useState, useMemo } from "react";
function ExpensiveCalculation() {
const [count, setCount] = useState(0);
// Use useMemo to memoize the result of the expensive calculation
const expensiveValue = useMemo(() => {
console.log("Expensive calculation is running...");
return count * 1000; // Simulate expensive calculation
}, [count]); // Recalculate only when 'count' changes
return (
<div>
<p>Count: {count}</p>
<p>Expensive Calculation: {expensiveValue}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default ExpensiveCalculation;
useOptimistic
import React, { useState } from "react";
function OptimisticDelete({ itemId }) {
const [items, setItems] = useState([{ id: 1, name: "Item 1" }, { id: 2, name: "Item 2" }]);
const [deletingId, setDeletingId] = useState(null);
const handleDelete = async (id) => {
// Optimistically remove the item from the list
setItems((prevItems) => prevItems.filter((item) => item.id !== id));
setDeletingId(id);
try {
// Simulate a network request
await fakeDeleteRequest(id);
// If the request succeeds, do nothing (item is already deleted)
} catch (error) {
// If the request fails, revert the optimistic update
setItems((prevItems) => [...prevItems, { id, name: `Item ${id}` }]);
setDeletingId(null);
alert("Failed to delete the item");
}
};
const fakeDeleteRequest = (id) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("Item deleted");
} else {
reject("Error deleting item");
}
}, 1000);
});
return (
<div>
<ul>
{items.map((item) => (
<li key={item.id}>
{item.name}
{deletingId === item.id ? (
<span> (Deleting...)</span>
) : (
<button onClick={() => handleDelete(item.id)}>Delete</button>
)}
</li>
))}
</ul>
</div>
);
}
export default OptimisticDelete;
useReducer
import React, { useReducer } from "react";
// Define the reducer function
const counterReducer = (state, action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
};
function Counter() {
// Initialize state with useReducer
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>Increment</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
</div>
);
}
export default Counter;
useRef
import React, { useRef } from "react";
function FocusInput() {
// Create a ref for the input element
const inputRef = useRef(null);
const handleFocus = () => {
// Focus the input field directly via the ref
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Focus me on button click" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default FocusInput;
useState
import React, { useState } from "react";
function Counter() {
// Declare a state variable 'count' with an initial value of 0
const [count, setCount] = useState(0);
const increment = () => {
// Update the state by incrementing the count
setCount(count + 1);
};
const decrement = () => {
// Update the state by decrementing the count
setCount(count - 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
export default Counter;
useSyncExternalStore
import React, { useSyncExternalStore, useState } from "react";
// A simple external store (a custom state management solution)
let listeners = [];
let count = 0;
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter((l) => l !== listener);
};
};
const getSnapshot = () => count;
const increment = () => {
count++;
listeners.forEach((listener) => listener()); // Notify subscribers
};
function Counter() {
// Use useSyncExternalStore to subscribe to the external store
const state = useSyncExternalStore(subscribe, getSnapshot);
return (
<div>
<p>Count: {state}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
export default Counter;
useTransition
import React, { useState, useTransition } from "react";
function SearchComponent() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
// Start transition hook
const [isPending, startTransition] = useTransition();
const handleChange = (event) => {
const value = event.target.value;
setQuery(value);
// Wrap the expensive state update inside startTransition
startTransition(() => {
// Simulate an expensive fetch or processing operation
const filteredResults = simulateSearch(value);
setResults(filteredResults);
});
};
// Simulate a slow search operation
const simulateSearch = (query) => {
return ["apple", "banana", "cherry", "date"].filter((item) =>
item.toLowerCase().includes(query.toLowerCase())
);
};
return (
<div>
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Search..."
/>
<p>{isPending ? "Loading..." : "Results:"}</p>
<ul>
{results.map((result) => (
<li key={result}>{result}</li>
))}
</ul>
</div>
);
}
export default SearchComponent;
Components
<Fragment> (<>...</>)
// Using <Fragment>
<Fragment>
<ChildComponent />
<AnotherChild />
</Fragment>
// Using shorthand <>
<>
<ChildComponent />
<AnotherChild />
</>
<Profiler>
import React, { useState, Profiler } from "react";
// Function to log performance data
const handleRender = (
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
interactions
) => {
console.log(`Rendering ${id}`);
console.log(`Phase: ${phase}`);
console.log(`Actual duration: ${actualDuration}ms`);
console.log(`Base duration: ${baseDuration}ms`);
console.log(`Start time: ${startTime}`);
console.log(`Commit time: ${commitTime}`);
console.log(`Interactions: ${interactions}`);
};
function App() {
const [count, setCount] = useState(0);
return (
<Profiler id="App" onRender={handleRender}>
<div>
<h1>Hello, React Profiler!</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
<p>Count: {count}</p>
</div>
</Profiler>
);
}
export default App;
<StrictMode>
import React, { StrictMode } from "react";
import ReactDOM from "react-dom";
import App from "./App";
// Wrap the root component with StrictMode
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
document.getElementById("root")
);
<Suspense>
import React, { Suspense } from "react";
// Simulate fetching data
function fetchData() {
return new Promise((resolve) =>
setTimeout(() => resolve("Fetched data!"), 2000)
);
}
function DataComponent() {
const data = fetchData(); // This is a synchronous operation for simplicity.
return <p>{data}</p>;
}
function App() {
return (
<div>
<h1>Fetching Data with Suspense</h1>
<Suspense fallback={<div>Loading data...</div>}>
<DataComponent />
</Suspense>
</div>
);
}
export default App;
APIs
act
import React, { useState } from "react";
import { render, fireEvent } from "@testing-library/react";
import { act } from "react-dom/test-utils";
// A simple component with a button that toggles text
function ToggleText() {
const [isToggled, setIsToggled] = useState(false);
return (
<div>
<p>{isToggled ? "Toggled!" : "Not Toggled"}</p>
<button onClick={() => setIsToggled(!isToggled)}>Toggle</button>
</div>
);
}
test("toggle text when button is clicked", () => {
const { getByText } = render(<ToggleText />);
// Initially, the text should say "Not Toggled"
expect(getByText("Not Toggled")).toBeInTheDocument();
// Simulate a click event and update the state inside act()
act(() => {
fireEvent.click(getByText("Toggle"));
});
// After clicking, the text should change to "Toggled!"
expect(getByText("Toggled!")).toBeInTheDocument();
});
cache
import React from "react";
import { useQuery } from "react-query";
// Function to fetch data
const fetchData = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
return res.json();
};
function App() {
// Fetch data with React Query, caching the result
const { data, isLoading, isError } = useQuery("posts", fetchData, {
cacheTime: 10000, // Cache data for 10 seconds
staleTime: 5000, // The data becomes stale after 5 seconds
});
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error loading data</div>;
return (
<div>
<h1>Posts</h1>
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default App;
createContext
import React, { createContext, useState } from "react";
// Create a context with a default value
const ThemeContext = createContext("light");
function App() {
const [theme, setTheme] = useState("light");
return (
// The Provider component makes the context value available to child components
<ThemeContext.Provider value={{ theme, setTheme }}>
<div>
<h1>Context Example</h1>
<ThemeSwitcher />
</div>
</ThemeContext.Provider>
);
}
function ThemeSwitcher() {
// Use the context value with useContext
const { theme, setTheme } = React.useContext(ThemeContext);
return (
<div>
<p>Current theme: {theme}</p>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle Theme
</button>
</div>
);
}
export default App;
lazy
import React, { createContext, useState, useContext, Suspense } from "react";
// Create a context for the theme
const ThemeContext = createContext();
// Provider Component
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Consumer Component that uses the context value
function ThemeSwitcher() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div>
<p>Current Theme: {theme}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
// App component with Suspense for lazy loading
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<ThemeProvider>
<ThemeSwitcher />
</ThemeProvider>
</Suspense>
);
}
export default App;
memo
import React, { useState, useMemo } from "react";
function App() {
const [count, setCount] = useState(0);
const [toggle, setToggle] = useState(true);
// Memoizing an expensive calculation
const expensiveCalculation = useMemo(() => {
console.log("Expensive calculation is running...");
return count * 2; // Example of an expensive calculation
}, [count]); // Only recompute when 'count' changes
return (
<div>
<h1>useMemo Example</h1>
<p>Count: {count}</p>
<p>Expensive Calculation Result: {expensiveCalculation}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<button onClick={() => setToggle(!toggle)}>Toggle</button>
</div>
);
}
export default App;
startTransition
import React, { useState, startTransition } from "react";
function App() {
const [isPending, setIsPending] = useState(false);
const [items, setItems] = useState([]);
const [query, setQuery] = useState("");
// Simulating a slow update (like fetching data)
const handleSearch = (e) => {
setQuery(e.target.value);
// Start a transition for non-urgent updates
startTransition(() => {
setIsPending(true);
// Simulate a slow operation (e.g., fetching data)
setTimeout(() => {
setItems(generateItems(e.target.value));
setIsPending(false);
}, 2000); // Simulating a 2-second delay
});
};
// Function to generate items based on the query
const generateItems = (query) => {
return Array.from({ length: 1000 }, (_, index) => `${query} item ${index + 1}`);
};
return (
<div>
<h1>Search with startTransition</h1>
<input
type="text"
value={query}
onChange={handleSearch}
placeholder="Search items..."
/>
{isPending && <p>Loading...</p>}
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
export default App;
use
import { createContext, use } from 'react';
const ThemeContext = createContext(null);
export default function MyApp() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
)
}
function Form() {
return (
<Panel title="Welcome">
<Button show={true}>Sign up</Button>
<Button show={false}>Log in</Button>
</Panel>
);
}
function Panel({ title, children }) {
const theme = use(ThemeContext);
const className = 'panel-' + theme;
return (
<section className={className}>
<h1>{title}</h1>
{children}
</section>
)
}
function Button({ show, children }) {
if (show) {
const theme = use(ThemeContext);
const className = 'button-' + theme;
return (
<button className={className}>
{children}
</button>
);
}
return false
}
experimental_taintObjectReference
import {experimental_taintObjectReference} from 'react';
export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
experimental_taintObjectReference(
'Do not pass the entire user object to the client. ' +
'Instead, pick off the specific properties you need for this use case.',
user,
);
return user;
}
experimental_taintUniqueValue
import {experimental_taintUniqueValue} from 'react';
export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
experimental_taintUniqueValue(
'Do not pass a user session token to the client.',
user,
user.session.token
);
return user;
}
Hooks (react-dom@19 )
useFormStatus
import { useFormStatus } from "react-dom";
import { submitForm } from "./actions.js";
function Submit() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
);
}
function Form({ action }) {
return (
<form action={action}>
<Submit />
</form>
);
}
export default function App() {
return <Form action={submitForm} />;
}
Components (react-dom@19 )
Common components (e.g. <div>)
<div className="wrapper">Some content</div>
<form>
export default function Search() {
function search(formData) {
const query = formData.get("query");
alert(`You searched for '${query}'`);
}
return (
<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>
);
}
<input>
export default function MyForm() {
return (
<>
<label>
Text input: <input name="myInput" />
</label>
<hr />
<label>
Checkbox: <input type="checkbox" name="myCheckbox" />
</label>
<hr />
<p>
Radio buttons:
<label>
<input type="radio" name="myRadio" value="option1" />
Option 1
</label>
<label>
<input type="radio" name="myRadio" value="option2" />
Option 2
</label>
<label>
<input type="radio" name="myRadio" value="option3" />
Option 3
</label>
</p>
</>
);
}
<option>
export default function FruitPicker() {
return (
<label>
Pick a fruit:
<select name="selectedFruit">
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
</select>
</label>
);
}
<progress>
export default function App() {
return (
<>
<progress value={0} />
<progress value={0.5} />
<progress value={0.7} />
<progress value={75} max={100} />
<progress value={1} />
<progress value={null} />
</>
);
}
<select>
export default function FruitPicker() {
return (
<label>
Pick a fruit:
<select name="selectedFruit">
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
</select>
</label>
);
}
<textarea />
export default function NewPost() {
return (
<label>
Write your post:
<textarea name="postContent" rows={4} cols={40} />
</label>
);
}
<link>
import ShowRenderedHTML from './ShowRenderedHTML.js';
export default function BlogPage() {
return (
<ShowRenderedHTML>
<link rel="icon" href="favicon.ico" />
<link rel="pingback" href="http://www.example.com/xmlrpc.php" />
<h1>My Blog</h1>
<p>...</p>
</ShowRenderedHTML>
);
}
<meta>
import ShowRenderedHTML from './ShowRenderedHTML.js';
export default function SiteMapPage() {
return (
<ShowRenderedHTML>
<meta name="keywords" content="React" />
<meta name="description" content="A site map for the React website" />
<h1>Site Map</h1>
<p>...</p>
</ShowRenderedHTML>
);
}
<script>
import ShowRenderedHTML from './ShowRenderedHTML.js';
function Map({lat, long}) {
return (
<>
<script async src="map-api.js" onLoad={() => console.log('script loaded')} />
<div id="map" data-lat={lat} data-long={long} />
</>
);
}
export default function Page() {
return (
<ShowRenderedHTML>
<Map />
</ShowRenderedHTML>
);
}
<style>
import ShowRenderedHTML from './ShowRenderedHTML.js';
import { useId } from 'react';
function PieChart({data, colors}) {
const id = useId();
const stylesheet = colors.map((color, index) =>
`#${id} .color-${index}: { color: "${color}"; }`
).join();
return (
<>
<style href={"PieChart-" + JSON.stringify(colors)} precedence="medium">
{stylesheet}
</style>
<svg id={id}>
…
</svg>
</>
);
}
export default function App() {
return (
<ShowRenderedHTML>
<PieChart data="..." colors={['red', 'green', 'blue']} />
</ShowRenderedHTML>
);
}
<title>
import ShowRenderedHTML from './ShowRenderedHTML.js';
export default function ContactUsPage() {
return (
<ShowRenderedHTML>
<title>My Site: Contact Us</title>
<h1>Contact Us</h1>
<p>Email us at support@example.com</p>
</ShowRenderedHTML>
);
}
APIs (react-dom@19 )
createPortal
import { createPortal } from 'react-dom';
function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>This child is placed in the parent div.</p>
{createPortal(
<p>This child is placed in the document body.</p>,
document.body
)}
</div>
);
}
flushSync
import { flushSync } from 'react-dom';
flushSync(() => {
setSomething(123);
});
preconnect
import { preconnect } from 'react-dom';
function CallToAction() {
const onClick = () => {
preconnect('http://example.com');
startWizard();
}
return (
<button onClick={onClick}>Start Wizard</button>
);
}
prefetchDNS
import { prefetchDNS } from 'react-dom';
function CallToAction() {
const onClick = () => {
prefetchDNS('http://example.com');
startWizard();
}
return (
<button onClick={onClick}>Start Wizard</button>
);
}
preinit
import { preinit } from 'react-dom';
function CallToAction() {
const onClick = () => {
preinit("https://example.com/wizardStyles.css", {as: "style"});
startWizard();
}
return (
<button onClick={onClick}>Start Wizard</button>
);
}
preinitModule
import { preinitModule } from 'react-dom';
function CallToAction() {
const onClick = () => {
preinitModule("https://example.com/module.js", {as: "script"});
startWizard();
}
return (
<button onClick={onClick}>Start Wizard</button>
);
}
preload
import { preload } from 'react-dom';
function CallToAction() {
const onClick = () => {
preload("https://example.com/wizardStyles.css", {as: "style"});
startWizard();
}
return (
<button onClick={onClick}>Start Wizard</button>
);
}
preloadModule
import { preloadModule } from 'react-dom';
function CallToAction() {
const onClick = () => {
preloadModule("https://example.com/module.js", {as: "script"});
startWizard();
}
return (
<button onClick={onClick}>Start Wizard</button>
);
}
Client APIs (react-dom@19)
createRoot
import { createRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = createRoot(domNode);
hydrateRoot
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, reactNode);
Server APIs (react-dom@19)
renderToPipeableStream
import { renderToPipeableStream } from 'react-dom/server';
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
renderToReadableStream
import { renderToReadableStream } from 'react-dom/server';
async function handler(request) {
const stream = await renderToReadableStream(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(stream, {
headers: { 'content-type': 'text/html' },
});
}
renderToStaticMarkup
import { renderToStaticMarkup } from 'react-dom/server';
// The route handler syntax depends on your backend framework
app.use('/', (request, response) => {
const html = renderToStaticMarkup(<Page />);
response.send(html);
});
renderToString
import { renderToString } from 'react-dom/server';
// The route handler syntax depends on your backend framework
app.use('/', (request, response) => {
const html = renderToString(<App />);
response.send(html);
});
Static APIs (react-dom@19)
prerender
import { prerender } from 'react-dom/static';
async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}
prerenderToNodeStream
import { prerenderToNodeStream } from 'react-dom/static';
// The route handler syntax depends on your backend framework
app.use('/', async (request, response) => {
const { prelude } = await prerenderToNodeStream(<App />, {
bootstrapScripts: ['/main.js'],
});
response.setHeader('Content-Type', 'text/plain');
prelude.pipe(response);
});
Rules of React
Components and Hooks must be pure
import { useState, useEffect } from 'react';
function useTime() {
// 1. Keep track of the current date's state. `useState` receives an initializer function as its
// initial state. It only runs once when the hook is called, so only the current date at the
// time the hook is called is set first.
const [time, setTime] = useState(() => new Date());
useEffect(() => {
// 2. Update the current date every second using `setInterval`.
const id = setInterval(() => {
setTime(new Date()); // ✅ Good: non-idempotent code no longer runs in render
}, 1000);
// 3. Return a cleanup function so we don't leak the `setInterval` timer.
return () => clearInterval(id);
}, []);
return time;
}
export default function Clock() {
const time = useTime();
return <span>{time.toLocaleString()}</span>;
}
React calls Components and Hooks
function BlogPost() {
return <Layout><Article /></Layout>; // ✅ Good: Only use components in JSX
}
function BlogPost() {
return <Layout>{Article()}</Layout>; // 🔴 Bad: Never call them directly
}
function ChatInput() {
const useDataWithLogging = withLogging(useData); // 🔴 Bad: don't write higher order Hooks
const data = useDataWithLogging();
}
function ChatInput() {
const data = useDataWithLogging(); // ✅ Good: Create a new version of the Hook
}
function useDataWithLogging() {
// ... Create a new version of the Hook and inline the logic here
}
function ChatInput() {
return <Button useData={useDataWithLogging} /> // 🔴 Bad: don't pass Hooks as props
}
function ChatInput() {
return <Button />
}
function Button() {
const data = useDataWithLogging(); // ✅ Good: Use the Hook directly
}
function useDataWithLogging() {
// If there's any conditional logic to change the Hook's behavior, it should be inlined into
// the Hook
}
Rules of Hooks
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);
// ...
}
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);
}
}
React Server Components
Server Components
// bundle.js
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
function Page({page}) {
const [content, setContent] = useState('');
// NOTE: loads *after* first page render.
useEffect(() => {
fetch(`/api/content/${page}`).then((data) => {
setContent(data.content);
});
}, [page]);
return <div>{sanitizeHtml(marked(content))}</div>;
}
// api.js
app.get(`/api/content/:page`, async (req, res) => {
const page = req.params.page;
const content = await file.readFile(`${page}.md`);
res.send({content});
});
import marked from 'marked'; // Not included in bundle
import sanitizeHtml from 'sanitize-html'; // Not included in bundle
async function Page({page}) {
// NOTE: loads *during* render, when the app is built.
const content = await file.readFile(`${page}.md`);
return <div>{sanitizeHtml(marked(content))}</div>;
}
Server Functions
// Server Component
import Button from './Button';
function EmptyNote () {
async function createNoteAction() {
// Server Function
'use server';
await db.notes.create();
}
return <Button onClick={createNoteAction}/>;
}
"use client";
export default function Button({onClick}) {
console.log(onClick);
// {$$typeof: Symbol.for("react.server.reference"), $$id: 'createNoteAction'}
return <button onClick={() => onClick()}>Create Empty Note</button>
}
Directives
'use client';
import { useState } from 'react';
import { formatDate } from './formatters';
import Button from './button';
export default function RichTextEditor({ timestamp, text }) {
const date = formatDate(timestamp);
// ...
const editButton = <Button />;
// ...
}
async function addToCart(data) {
'use server';
// ...
}
Legacy React APIs
Children
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
cloneElement
import { Children, cloneElement, useState } from 'react';
export default function List({ children }) {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<div className="List">
{Children.map(children, (child, index) =>
cloneElement(child, {
isHighlighted: index === selectedIndex
})
)}
<hr />
<button onClick={() => {
setSelectedIndex(i =>
(i + 1) % Children.count(children)
);
}}>
Next
</button>
</div>
);
}
Component
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
createElement
import { createElement } from 'react';
function Greeting({ name }) {
return createElement(
'h1',
{ className: 'greeting' },
'Hello'
);
}
createRef
import { Component, createRef } from 'react';
export default class Form extends Component {
inputRef = createRef();
handleClick = () => {
this.inputRef.current.focus();
}
render() {
return (
<>
<input ref={this.inputRef} />
<button onClick={this.handleClick}>
Focus the input
</button>
</>
);
}
}
isValidElement
import { isValidElement, createElement } from 'react';
// ✅ React elements
console.log(isValidElement(<p />)); // true
console.log(isValidElement(createElement('p'))); // true
// ❌ Not React elements
console.log(isValidElement(25)); // false
console.log(isValidElement('Hello')); // false
console.log(isValidElement({ age: 42 })); // false
PureComponent
import { PureComponent } from 'react';
class Greeting extends PureComponent {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}