Stateless functional components introduced as pure functions from props to UI.
// Stateless functional componentconst Greeting = ({ name }) => <h1>Hello, {name}!</h1>;React and ReactDOM separated into two packages, enabling React Native and other renderers.
Ref callbacks allow direct DOM access.
<input ref={node => { this.input = node; }} />Rewrote the reconciler for incremental rendering. Foundation for all future concurrent features.
componentDidCatch and getDerivedStateFromError catch errors in the component tree.
class Boundary extends Component { static getDerivedStateFromError(error) { return { hasError: true }; }}Render children outside the parent DOM hierarchy.
ReactDOM.createPortal(children, document.body)Return multiple elements from render without a wrapping DOM node.
return <><h1>Title</h1><p>Body</p></>;Components can now return arrays of elements or plain strings from render.
createContext and Context.Provider replaced the old context API.
const ThemeContext = createContext('light');<ThemeContext.Provider value="dark">...</ThemeContext.Provider>Object-based ref API replacing callback refs for most use cases.
this.myRef = createRef();<input ref={this.myRef} />Replaces componentWillReceiveProps for syncing state from props.
componentWillMount, componentWillReceiveProps, componentWillUpdate marked UNSAFE_.
Code-split components with dynamic imports and show fallbacks during loading.
const OtherComponent = React.lazy(() => import('./Other'));<Suspense fallback={<Spinner />}> <OtherComponent /></Suspense>HOC equivalent of PureComponent for function components.
const Expensive = React.memo(function Expensive({ value }) { return <div>{value}</div>;});Subscribe to context in class components without Consumer wrapper.
Local state in function components.
const [count, setCount] = useState(0);Side effects and lifecycle in function components.
useEffect(() => { document.title = count; }, [count]);Context consumption, complex state, and mutable refs in functions.
Memoize values and functions for performance optimization.
Extract and share stateful logic between components without HOCs or render props.
function useWindowSize() { const [size, setSize] = useState({ width: 0, height: 0 }); useEffect(() => { const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight }); window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler); }, []); return size;}A "stepping stone" release focused on making future upgrades easier.
No longer need to import React in every file with JSX.
// Before React 17: requiredimport React from 'react';// After React 17: not needed for JSXfunction App() { return <h1>Hello</h1>; }Events now attach to the React root instead of document — fixes issues with multiple React versions.
Multiple versions of React can run on the same page, enabling gradual migration.
React can interrupt, pause, and resume renders to keep the UI responsive.
setState calls inside Promises, setTimeout, and native events are now automatically batched.
// React 17: two re-renderssetTimeout(() => { setCount(c => c + 1); setFlag(f => !f);}, 1000);// React 18: one re-render (automatic batching)Mark non-urgent updates to prioritize responsive interactions.
const [isPending, startTransition] = useTransition();startTransition(() => setFilter(input)); // interruptibleStreaming SSR with selective hydration — progressively stream HTML with Suspense boundaries.
Generate stable IDs for accessibility attributes, safe across server and client.
const id = useId(); // ':r0:' — stable across rendersNew root API replaces ReactDOM.render. Required to opt into React 18 features.
import { createRoot } from 'react-dom/client';createRoot(document.getElementById('root')).render(<App />);Read Promises or Context in the render function — can be called conditionally.
function UserProfile({ userPromise }) { const user = use(userPromise); // suspends until resolved return <h1>{user.name}</h1>;}Run-on-server components with direct data access and zero client bundle impact.
// No 'use client' = Server Componentasync function Page({ params }) { const data = await db.query(params.id); return <Layout data={data} />;}Async functions for form submissions and mutations with built-in pending/error state.
const [state, action, isPending] = useActionState(serverAction, null);ref can now be passed as a regular prop — forwardRef is no longer required.
// React 19: no forwardRef neededfunction Input({ ref, ...props }) { return <input ref={ref} {...props} />;}Show an optimistic state immediately while an async operation completes in the background.
const [optimisticLikes, addOptimisticLike] = useOptimistic( likes, (state, delta) => state + delta);title, meta, and link tags inside components are hoisted to <head> automatically.
function BlogPost({ post }) { return ( <article> <title>{post.title}</title> <meta name="description" content={post.excerpt} /> <h1>{post.title}</h1> </article> );}