React is a JavaScript library for building user interfaces through a declarative, component-based approach.
npx @tessl/cli install tessl/npm-react@19.2.0React is a JavaScript library for building user interfaces through a declarative, component-based approach.
npm install reactreact - Core React functionality for client-side renderingreact/jsx-runtime - JSX transformation (automatically used by compilers)react/jsx-dev-runtime - Development-mode JSX (includes additional debugging info)react/compiler-runtime - React Compiler optimization runtimereact.react-server.js - Server-only APIs for React Server ComponentsMost apps only import from react. JSX runtimes are automatically used by Babel/TypeScript compilers.
ESM:
import { useState, useEffect, useContext, useRef } from 'react';
import { Component, PureComponent, memo, lazy, Suspense } from 'react';CommonJS:
const { useState, useEffect } = require('react');
const React = require('react');function Counter() {
const [count, setCount] = useState(0);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</>
);
}
// State updates are batched automatically (React 18+)
// Multiple setState calls in event handlers only trigger one re-renderReact is renderer-agnostic and must be paired with a renderer (like react-dom) to display UI.
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];Basic state management:
const [state, setState] = useState(initialValue);
setState(newValue); // Direct update
setState(prev => prev + 1); // Functional update (use when new state depends on old)
const [state] = useState(() => expensiveInit()); // Lazy init (function runs only once)
// Common mistake: using state immediately after setState
setState(count + 1);
console.log(count); // ❌ Still shows old value (setState is async)
// Use useEffect with [count] to react to changesfunction useReducer<S, A>(
reducer: (state: S, action: A) => S,
initialArg: S,
init?: (initialArg: S) => S
): [S, Dispatch<A>];Complex state with reducer:
const [state, dispatch] = useReducer(reducer, initialValue);
dispatch({ type: 'increment' });function useRef<T>(initialValue: T): { current: T };Mutable refs that persist:
const ref = useRef(null); // DOM access
const timerRef = useRef(null); // Storing timersfunction useEffect(effect: () => (void | (() => void)), deps?: ReadonlyArray<any>): void;Side effects after render:
useEffect(() => {
// Effect runs after render
return () => cleanup(); // Cleanup function
}, [deps]); // Run when deps change
// Note: In StrictMode (dev), effects run twice to help find bugsCommon patterns:
// Data fetching with abort
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') setError(err);
});
return () => controller.abort(); // Cancel on cleanup
}, [url]);
// Subscriptions
useEffect(() => {
const sub = api.subscribe(onUpdate);
return () => sub.unsubscribe();
}, []);
// Event listeners
useEffect(() => {
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
// ⚠️ Infinite loop trap
useEffect(() => {
setData([...data, item]); // ❌ data in deps causes infinite loop
}, [data]); // Use functional update or useReducer insteadfunction useLayoutEffect(effect: () => (void | (() => void)), deps?: ReadonlyArray<any>): void;Synchronous effects before paint (for DOM measurements):
useLayoutEffect(() => {
const height = ref.current.getBoundingClientRect().height;
setHeight(height);
}, []);function useInsertionEffect(effect: () => (void | (() => void)), deps?: ReadonlyArray<any>): void;Runs before all DOM mutations (for CSS-in-JS):
useInsertionEffect(() => {
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
return () => style.remove();
}, [css]);function useMemo<T>(factory: () => T, deps: ReadonlyArray<any>): T;Memoizes expensive computations:
const filtered = useMemo(() =>
items.filter(item => item.name.includes(filter)),
[items, filter]
);
// ⚠️ Only use for expensive operations - premature optimization adds overhead
// Profile first, then optimizefunction useCallback<T>(callback: T, deps: ReadonlyArray<any>): T;Stable function references:
const handleClick = useCallback(() => doSomething(id), [id]);
// Prevents child re-renders when passed as propfunction useDeferredValue<T>(value: T, initialValue?: T): T;Defers non-urgent updates:
const deferredQuery = useDeferredValue(query);
// query updates immediately, deferredQuery lagsfunction useId(): string;Unique IDs for accessibility:
const id = useId();
return <><label htmlFor={id}>Name</label><input id={id} /></>;function useTransition(): [boolean, (callback: () => void, options?: StartTransitionOptions) => void];Non-urgent state updates:
const [isPending, startTransition] = useTransition();
startTransition(() => setState(newValue)); // Low priority updatefunction use<T>(resource: Promise<T> | Context<T>): T;Unwraps promises (supports Suspense):
const user = use(userPromise); // Suspends until resolvedfunction useOptimistic<S, A>(passthrough: S, reducer?: (state: S, action: A) => S): [S, (action: A) => void];Optimistic UI updates:
const [optimisticState, addOptimistic] = useOptimistic(state, reducer);
addOptimistic(newItem); // Immediately updates UI
try { await save(newItem); } catch { /* reverts */ }function useActionState<S, P>(
action: (state: S, payload: P) => S | Promise<S>,
initialState: S,
permalink?: string
): [S, (payload: P) => void, boolean];Server action state management:
const [state, formAction, isPending] = useActionState(updateProfile, {success: false});
return <form action={formAction}><button disabled={isPending}>Save</button></form>;function useContext<T>(context: Context<T>): T;Read context values:
const theme = useContext(ThemeContext);function useSyncExternalStore<T>(
subscribe: (onStoreChange: () => void) => (() => void),
getSnapshot: () => T,
getServerSnapshot?: () => T
): T;Subscribe to external stores:
const isOnline = useSyncExternalStore(
(cb) => { window.addEventListener('online', cb); window.addEventListener('offline', cb);
return () => { window.removeEventListener('online', cb); window.removeEventListener('offline', cb); } },
() => navigator.onLine,
() => true // SSR fallback
);function useImperativeHandle<T>(ref: Ref<T>, createHandle: () => T, deps?: ReadonlyArray<any>): void;Customize ref value:
const Input = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => inputRef.current.value = ''
}));
return <input ref={inputRef} />;
});function useDebugValue<T>(value: T, format?: (value: T) => any): void;Label custom hooks in DevTools:
useDebugValue(isOnline ? '🟢 Online' : '🔴 Offline');function useEffectEvent<T>(handler: T): T;Stable event handlers for effects:
const onMessage = useEffectEvent((msg) => log(msg, theme)); // Always reads latest theme
useEffect(() => connection.on('message', onMessage), [connection]); // theme not in depsclass Component<P = {}, S = {}> {
constructor(props: P);
setState(updater: S | ((prevState: S, props: P) => S), callback?: () => void): void;
forceUpdate(callback?: () => void): void;
props: Readonly<P>;
state: Readonly<S>;
}
class PureComponent<P = {}, S = {}> extends Component<P, S> {}Class component:
class Counter extends Component {
state = { count: 0 };
increment = () => this.setState({ count: this.state.count + 1 });
render() { return <button onClick={this.increment}>{this.state.count}</button>; }
}const Fragment: ComponentType<{ children?: ReactNode; key?: Key }>;
const StrictMode: ComponentType<{ children?: ReactNode }>;
const Suspense: ComponentType<{ children?: ReactNode; fallback?: ReactNode }>;
const Profiler: ComponentType<{
id: string;
onRender?: (id, phase, actualDuration, baseDuration, startTime, commitTime) => void;
children?: ReactNode;
}>;Usage:
<Fragment><Child /></Fragment> // or <>
<StrictMode><App /></StrictMode>
<Suspense fallback={<Loader />}><LazyComponent /></Suspense>
<Profiler id="App" onRender={logMetrics}><App /></Profiler>function createElement<P>(
type: string | ComponentType<P>,
props?: (P & { key?: Key; ref?: Ref<any> }) | null,
...children: ReactNode[]
): ReactElement<P>;
function cloneElement<P>(
element: ReactElement<P>,
props?: Partial<P> & { key?: Key; ref?: Ref<any> },
...children: ReactNode[]
): ReactElement<P>;
function isValidElement(object: any): boolean;Rarely used directly (JSX preferred):
createElement('div', { className: 'container' }, 'Hello');
cloneElement(child, { className: 'enhanced' });
isValidElement(value); // Type guardfunction createRef<T>(): RefObject<T>;
function forwardRef<P, T>(render: (props: P, ref: Ref<T>) => ReactElement | null): ComponentType<P & { ref?: Ref<T> }>;Usage:
const ref = createRef();
<input ref={ref} />
ref.current.focus();
const Input = forwardRef((props, ref) => <input ref={ref} {...props} />);function createContext<T>(defaultValue: T): Context<T>;Usage:
const ThemeContext = createContext('light');
function App() {
return <ThemeContext.Provider value="dark"><Children /></ThemeContext.Provider>;
}
function Child() {
const theme = useContext(ThemeContext); // "dark"
}function memo<P>(
Component: ComponentType<P>,
propsAreEqual?: (prevProps: P, nextProps: P) => boolean
): ComponentType<P>;Prevent re-renders:
const Memoized = memo(({ data }) => <div>{data.map(...)}</div>);function lazy<P>(factory: () => Promise<{ default: ComponentType<P> }>): ComponentType<P>;Code splitting:
const LazyComponent = lazy(() => import('./Component'));
<Suspense fallback={<Loader />}><LazyComponent /></Suspense>const Children = {
map<T, C>(children: C | ReadonlyArray<C>, fn: (child: C, index: number) => T): T[];
forEach<C>(children: C | ReadonlyArray<C>, fn: (child: C, index: number) => void): void;
count(children: any): number;
only<C>(children: C): C;
toArray<C>(children: C | ReadonlyArray<C>): C[];
};Usage:
Children.map(children, (child, i) => cloneElement(child, { index: i }));
Children.count(children); // Number of children
Children.only(children); // Asserts exactly one child
Children.toArray(children); // Flatten to arrayfunction startTransition(callback: () => void, options?: StartTransitionOptions): void;Mark updates as non-urgent:
startTransition(() => setState(newValue));
// Useful when you don't have useTransition hook availablefunction cache<A extends ReadonlyArray<any>, T>(fn: (...args: A) => T): (...args: A) => T;
function cacheSignal(): AbortSignal | null;Server-side caching (request-scoped):
const getUser = cache(async (id) => {
const res = await fetch(`/api/users/${id}`);
return res.json();
});
// Multiple calls in same request share resultfunction act(callback: () => void | Promise<void>): Promise<void>;
function captureOwnerStack(): string | null;
const version: string;Testing and debugging:
await act(() => render(<Component />)); // Testing
const stack = captureOwnerStack(); // Debug (dev only)
const version = version; // "19.2.0"Warning: Use with caution, may change:
Activity - Activity boundaries for state preservationunstable_useCacheRefresh - Cache invalidationexperimental_taintUniqueValue - Prevent data leakage (server-only)experimental_taintObjectReference - Taint objects (server-only)unstable_postpone - Postpone rendering (server-only)See Experimental APIs for details.
type ReactNode = ReactElement | string | number | boolean | null | undefined | ReactNode[];
type ReactElement<P = any> = { type: string | ComponentType<P>; props: P; key: Key | null };
type Key = string | number;
type Ref<T> = { current: T | null } | ((instance: T | null) => void) | null;
type ComponentType<P = {}> = ((props: P) => ReactElement | null) | (new (props: P) => Component<P>);
type Dispatch<A> = (action: A) => void;
type SetStateAction<S> = S | ((prevState: S) => S);function useFetch(url) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url).then(res => res.json()).then(setData);
}, [url]);
return data;
}class ErrorBoundary extends Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught:', error, errorInfo);
// Log to error reporting service
}
render() {
if (this.state.hasError) {
return <div>Something went wrong: {this.state.error?.message}</div>;
}
return this.props.children;
}
}
// Note: No function component equivalent yet
// Catches errors in: rendering, lifecycle methods, constructors
// Doesn't catch: event handlers, async code, SSR errors// Controlled
const [value, setValue] = useState('');
<input value={value} onChange={e => setValue(e.target.value)} />
// Uncontrolled
const inputRef = useRef();
// Access via inputRef.current.value{condition && <Component />}
{condition ? <True /> : <False />}
{items.map(item => <Item key={item.id} {...item} />)}const [form, setForm] = useState({ name: '', email: '' });
<form onSubmit={e => { e.preventDefault(); handleSubmit(form); }}>
<input value={form.name} onChange={e => setForm({...form, name: e.target.value})} />
</form>// Always provide stable keys
{items.map(item => <Item key={item.id} {...item} />)}
// Keys help React identify which items changed
// ✅ Use stable IDs from data (item.id)
// ❌ Don't use array indices (causes bugs when list changes)
// ❌ Don't use random values (Math.random(), Date.now())
// Keys must be unique among siblings (not globally)// Avoid wrapper divs
<>
<Header />
<Content />
</>
// or
import { Fragment } from 'react';
<Fragment key={id}>{children}</Fragment>