React.js is a powerful JavaScript library powering countless web applications you use daily, from social media platforms to streaming services. Notably, Netflix, Airbnb, and Atlassian rely on React.js to build dynamic, scalable interfaces.
React's component-based architecture sets it apart. It enables you to construct user interfaces from reusable, independent components, each of which maintains its own state. This modular approach has driven widespread adoption; Stack Overflow's 2024 Developer Survey shows more than 40% of professional developers use React. Its declarative style allows you to describe what the UI should look like based on the application's current state, eliminating the complexity of manually manipulating UI changes.
In brief
React's strength comes from essential concepts working seamlessly:
These core principles make React powerful, distinctive, and developer-friendly.
React embraces declarative programming, allowing you to describe what the UI should look like instead of detailing each step required to update the DOM.
Compare these two approaches:
1// Imperative approach (vanilla JS)
2const container = document.getElementById('container');
3const button = document.createElement('button');
4button.className = 'btn';
5button.onclick = function() {
6 const text = document.createTextNode('Clicked!');
7 container.appendChild(text);
8}
9button.textContent = 'Click Me';
10container.appendChild(button);
11
12// Declarative approach (React)
13function Button() {
14 const [clicked, setClicked] = useState(false);
15 return (
16 <div>
17 <button className="btn" onClick={() => setClicked(true)}>
18 Click Me
19 </button>
20 {clicked && 'Clicked!'}
21 </div>
22 );
23}This approach makes code more predictable and easier to debug. When issues arise, you can trace them back to specific components and states rather than hunting through complex DOM manipulation sequences.
React encourages you to build UIs from small, reusable, independent components. Each component encapsulates its structure and behavior. This building-block approach lets developers:
In React, components form a hierarchical tree structure, with data flowing from parent components down to their children through props:
1function App() {
2 return (
3 <div>
4 <Header title="My React App" />
5 <Content>
6 <UserProfile username="reactfan" />
7 <UserStats level={42} />
8 </Content>
9 <Footer year={2025} />
10 </div>
11 );
12}This structure closely mirrors how designers naturally think about UI elements, making development intuitive. For instance, Airbnb's engineering team has reported that adopting component-based development significantly improved development speed and maintainability, though specific figures for code duplication reduction have not been made public.
JavaScript XML (JSX) lets you write HTML-like syntax directly within JavaScript, fundamentally changing how you create UIs in React. Instead of separating markup and logic, JSX intuitively combines them.
Here's what JSX looks like in practice:
1// JSX syntax
2const element = (
3 <div className="greeting">
4 <h1>Hello, {user.name}!</h1>
5 <p>We're glad to see you again.</p>
6 </div>
7);
8
9// Compiled JavaScript (what React actually sees)
10const element = React.createElement(
11 'div',
12 { className: 'greeting' },
13 React.createElement('h1', null, 'Hello, ', user.name, '!'),
14 React.createElement('p', null, "We're glad to see you again.")
15);Although JSX resembles HTML, it offers the complete flexibility of JavaScript. You can:
Unlike template languages used in other frameworks, JSX compiles to regular JavaScript function calls. This brings type safety and the same error and warning messages you'd get with regular JavaScript.
React 19 introduces the React Compiler, a game-changing feature that automatically optimizes your components. Previously, developers needed to manually use useMemo, useCallback, and React.memo to prevent unnecessary re-renders. The compiler now handles this automatically.
1// React 19: No manual optimization needed
2function TodoList({ items, filter }) {
3 // The compiler automatically optimizes this
4 const filteredItems = items.filter(item => item.status === filter);
5
6 return (
7 <ul>
8 {filteredItems.map(item => (
9 <li key={item.id}>{item.text}</li>
10 ))}
11 </ul>
12 );
13}The React Compiler analyzes your code at build time and automatically applies optimizations, making your apps faster without extra work. This technology is designed for large-scale production use cases like Instagram, but there is no official confirmation that it is currently powering Instagram in production.
React's true power emerges when creating interfaces that respond to user input and changing data. This interactivity relies on props, state, and the new Actions API.
Props (short for properties) pass data from parent to child components. They follow a one-way data flow, ensuring your application's behavior remains predictable.
1// Parent component passing props
2function BookList() {
3 const books = [
4 { id: 1, title: 'React Explained', author: 'Zac Gordon' },
5 { id: 2, title: 'Learning React', author: 'Alex Banks & Eve Porcello' }
6 ];
7
8 return (
9 <div>
10 <h2>Recommended Books</h2>
11 {books.map(book => (
12 <Book key={book.id} title={book.title} author={book.author} />
13 ))}
14 </div>
15 );
16}
17
18// Child component receiving props
19function Book({ title, author }) {
20 return (
21 <div className="book">
22 <h3>{title}</h3>
23 <p>By {author}</p>
24 </div>
25 );
26}In React 19, refs still require the use of forwardRef to be passed to child components; passing refs as regular props is not supported:
1// React 19: ref as a prop
2function MyInput({ placeholder, ref }) {
3 return <input placeholder={placeholder} ref={ref} />;
4}
5
6// Usage
7function Form() {
8 const inputRef = useRef(null);
9
10 return <MyInput ref={inputRef} placeholder="Enter text" />;
11}React 19 introduces new features like useEffectEvent and cacheSignal for improved resource management, but it does not support returning a cleanup function from ref callbacks:
1<input
2 ref={(ref) => {
3 // ref created - set up event listeners, etc.
4
5 // NEW: return a cleanup function
6 return () => {
7 // ref cleanup - remove listeners, free resources
8 };
9 }}
10/>When the component unmounts, React automatically calls the cleanup function, preventing memory leaks and ensuring proper resource management.
While props handle external data passed into a component, state handles internal data that changes over time, like user inputs or dynamic content fetched from APIs. Here's a basic example demonstrating state management with the useState hook:
1function Counter() {
2 const [count, setCount] = useState(0);
3
4 return (
5 <div>
6 <p>You clicked {count} times</p>
7 <button onClick={() => setCount(count + 1)}>
8 Click me
9 </button>
10 </div>
11 );
12}Whenever state changes, React automatically re-renders the component to reflect the new values.
React 19 introduces Actions, a new pattern for handling asynchronous operations like form submissions and data mutations. Actions automatically manage pending states, errors, and sequential requests:
1function UpdateName() {
2 const [name, setName] = useState("");
3 const [isPending, startTransition] = useTransition();
4
5 const handleSubmit = () => {
6 startTransition(async () => {
7 const error = await updateName(name);
8 if (error) {
9 // Handle error
10 return;
11 }
12 // Redirect on success
13 });
14 };
15
16 return (
17 <div>
18 <input value={name} onChange={(e) => setName(e.target.value)} />
19 <button onClick={handleSubmit} disabled={isPending}>
20 Update
21 </button>
22 </div>
23 );
24}For forms, use the useActionState hook for simplified state management:
1function ChangeName() {
2 const [error, submitAction, isPending] = useActionState(
3 async (previousState, formData) => {
4 const error = await updateName(formData.get("name"));
5 if (error) {
6 return error;
7 }
8 redirect("/path");
9 return null;
10 },
11 null
12 );
13
14 return (
15 <form action={submitAction}>
16 <input type="text" name="name" />
17 <button type="submit" disabled={isPending}>Update</button>
18 {error && <p>{error}</p>}
19 </form>
20 );
21}The useFormStatus hook provides access to form status in child components without prop drilling:
1import { useFormStatus } from 'react-dom';
2
3function SubmitButton() {
4 const { pending } = useFormStatus();
5
6 return (
7 <button type="submit" disabled={pending}>
8 {pending ? 'Submitting...' : 'Submit'}
9 </button>
10 );
11}
12
13function MyForm() {
14 return (
15 <form action={handleSubmit}>
16 <input name="username" />
17 <SubmitButton />
18 </form>
19 );
20}The useOptimistic hook enables instant UI feedback while waiting for server responses:
1function ChangeName({ currentName, onUpdateName }) {
2 const [optimisticName, setOptimisticName] = useOptimistic(currentName);
3
4 const submitAction = async formData => {
5 const newName = formData.get("name");
6 setOptimisticName(newName);
7 const updatedName = await updateName(newName);
8 onUpdateName(updatedName);
9 };
10
11 return (
12 <form action={submitAction}>
13 <p>Your name is: {optimisticName}</p>
14 <label>Change Name:</label>
15 <input
16 type="text"
17 name="name"
18 disabled={currentName !== optimisticName}
19 />
20 </form>
21 );
22}As your React applications grow, you'll need more advanced techniques to effectively manage resources, state, and navigation.
React 19 introduces the use() hook for reading resources like Promises and Context. Unlike other hooks, use() can be called conditionally:
1import { use } from 'react';
2
3// Reading a Promise
4function Comments({ commentsPromise }) {
5 const comments = use(commentsPromise);
6
7 return comments.map(comment => <p key={comment.id}>{comment}</p>);
8}
9
10// Reading Context conditionally
11function Heading({ children }) {
12 if (children == null) {
13 return null;
14 }
15
16 // This works with use() but wouldn't with useContext
17 const theme = use(ThemeContext);
18
19 return (
20 <h1 style={{ color: theme.color }}>
21 {children}
22 </h1>
23 );
24}React 19 includes all existing hooks plus new additions:
Core Hooks:
useState: Manage component state useEffect: Handle side effects useRef: Create mutable references useTransition: Mark state updates as transitionsNew React 19 Hooks:
use(): Read Promises and Context conditionally useActionState: Manage form submission state useOptimistic: Implement optimistic updates useFormStatus: Access form pending stateAdditional Hooks:
useDeferredValue: Defer non-urgent updates (now supports initial values) useReducer: Manage complex state useMemo and useCallback: Performance optimizations (less needed with React Compiler)React 19 simplifies Context usage. You can now use Context directly as a provider without .Provider:
1const ThemeContext = createContext('light');
2
3// React 19: Use Context directly as provider
4function App() {
5 const [theme, setTheme] = useState('light');
6
7 return (
8 <ThemeContext value={theme}>
9 <Header />
10 <MainContent />
11 <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
12 Toggle Theme
13 </button>
14 </ThemeContext>
15 );
16}
17
18// Consume with use() hook
19function ThemedButton() {
20 const theme = use(ThemeContext);
21
22 return (
23 <button className={`btn btn-${theme}`}>
24 Themed Button
25 </button>
26 );
27}React 19 introduces a Metadata API that lets you define document metadata in components, and React handles ensuring this metadata appears in the document's \<head> section:
1function BlogPost({ post }) {
2 return (
3 <article>
4 <title>{post.title}</title>
5 <meta name="author" content="Josh" />
6 <link rel="author" href="https://twitter.com/joshcstory/" />
7 <meta name="keywords" content={post.keywords} />
8
9 <h1>{post.title}</h1>
10 <p>{post.content}</p>
11 </article>
12 );
13}React automatically hoists <title>, <link>, and <meta> tags to the document's <head> section.
React 19 introduces optimizations for rendering and data fetching, but it does not provide built-in support for stylesheet precedence control or automatic deduplication of scripts and stylesheets:
1function ComponentOne() {
2 return (
3 <Suspense fallback="loading...">
4 <link rel="stylesheet" href="foo" precedence="default" />
5 <link rel="stylesheet" href="bar" precedence="high" />
6 <article className="foo-class bar-class">
7 {/* Content */}
8 </article>
9 </Suspense>
10 );
11}React 19 also supports async scripts anywhere in your component tree:
1function MyComponent() {
2 return (
3 <div>
4 <script async={true} src="https://example.com/script.js" />
5 <p>Hello World</p>
6 </div>
7 );
8}React automatically deduplicates scripts and stylesheets, ensuring they're only loaded once.
React 19 focuses on improving performance through new server-side pre-rendering and streaming APIs, but does not provide built-in APIs for resource preloading:
1import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';
2
3function MyComponent() {
4 preinit('https://example.com/script.js', { as: 'script' });
5 preload('https://example.com/font.woff', { as: 'font' });
6 preload('https://example.com/style.css', { as: 'style' });
7 prefetchDNS('https://api.example.com');
8 preconnect('https://cdn.example.com');
9
10 return <div>Content</div>;
11}React 19 includes stable Server Components support. Server Components run on the server during build time or per-request, enabling direct database access and reducing client-side JavaScript.
Use the "use client" directive to mark Client Components:
1'use client';
2
3function InteractiveButton() {
4 const [count, setCount] = useState(0);
5
6 return (
7 <button onClick={() => setCount(count + 1)}>
8 Clicked {count} times
9 </button>
10 );
11}Server Actions allow Client Components to call server functions using the "use server" directive:
1async function createPost(formData) {
2 'use server';
3
4 const post = await db.posts.create({
5 title: formData.get('title'),
6 content: formData.get('content')
7 });
8
9 return post;
10}Frameworks like Next.js 16 and, to some extent, Remix support React 19's Server Components and Actions.
React 19 adds official support for native usage of Web Components and Custom Elements, improving interoperability and TypeScript integration, but does not provide full built-in handling for all interoperability aspects such as property versus attribute distinctions:
1function MyApp() {
2 return <my-custom-element prop1="value" prop2={complexObject} />;
3}React now correctly handles custom element properties versus attributes, making it seamless to use Web Components in React applications.
React 19 improves error handling with:
onCaughtError, onUncaughtError, onRecoverableError1const root = createRoot(document.getElementById('root'), {
2 onCaughtError: (error, errorInfo) => {
3 console.error('Caught error:', error, errorInfo);
4 },
5 onUncaughtError: (error, errorInfo) => {
6 console.error('Uncaught error:', error, errorInfo);
7 }
8});React has gained widespread adoption due to its clear benefits:
Before you start building with React, you'll need Node.js (version 18 or later) and npm installed on your system.
For new projects, use Vite for fast development:
1npm create vite@latest my-app -- --template react
2cd my-app
3npm install
4npm run devFor production applications with Server Components and SSR, use Next.js:
1npx create-next-app@latest my-app
2cd my-app
3npm run devLet's build a counter component using React 19 features:
1// Counter.jsx
2import { useState } from 'react';
3
4function Counter() {
5 const [count, setCount] = useState(0);
6
7 // React 19 compiler automatically optimizes these
8 const increment = () => setCount(count + 1);
9 const decrement = () => setCount(count - 1);
10 const reset = () => setCount(0);
11
12 return (
13 <div className="counter">
14 <h2>Counter: {count}</h2>
15 <button onClick={decrement}>Decrease</button>
16 <button onClick={increment}>Increase</button>
17 <button onClick={reset}>Reset</button>
18 </div>
19 );
20}
21
22export default Counter;Integrating React with a headless CMS like Strapi empowers you to manage content seamlessly. Here's how to fetch and display blog posts:
1import { useState, useEffect } from 'react';
2
3function BlogPosts() {
4 const [posts, setPosts] = useState([]);
5 const [loading, setLoading] = useState(true);
6 const [error, setError] = useState(null);
7
8 useEffect(() => {
9 fetch('http://localhost:1337/api/posts')
10 .then(response => {
11 if (!response.ok) throw new Error('Failed to fetch');
12 return response.json();
13 })
14 .then(data => {
15 setPosts(data.data);
16 setLoading(false);
17 })
18 .catch(error => {
19 setError(error);
20 setLoading(false);
21 });
22 }, []);
23
24 if (loading) return <p>Loading posts...</p>;
25 if (error) return <p>Error: {error.message}</p>;
26
27 return (
28 <div className="blog-posts">
29 <h2>Latest Blog Posts</h2>
30 {posts.map(post => (
31 <article key={post.id}>
32 <h3>{post.attributes.title}</h3>
33 <p>{post.attributes.summary}</p>
34 <a href={`/posts/${post.id}`}>Read more</a>
35 </article>
36 ))}
37 </div>
38 );
39}
40
41export default BlogPosts;Using React 19 Actions with Strapi:
1function CreatePost() {
2 const [state, submitAction, isPending] = useActionState(
3 async (prevState, formData) => {
4 const token = localStorage.getItem('token');
5
6 try {
7 const response = await fetch('http://localhost:1337/api/posts', {
8 method: 'POST',
9 headers: {
10 'Content-Type': 'application/json',
11 'Authorization': `Bearer ${token}`
12 },
13 body: JSON.stringify({
14 data: {
15 title: formData.get('title'),
16 content: formData.get('content')
17 }
18 })
19 });
20
21 if (!response.ok) {
22 return { error: 'Failed to create post' };
23 }
24
25 return { success: true };
26 } catch (error) {
27 return { error: error.message };
28 }
29 },
30 { error: null, success: false }
31 );
32
33 return (
34 <form action={submitAction}>
35 <input name="title" placeholder="Post title" required />
36 <textarea name="content" placeholder="Post content" required />
37 <button type="submit" disabled={isPending}>
38 {isPending ? 'Creating...' : 'Create Post'}
39 </button>
40 {state.error && <p className="error">{state.error}</p>}
41 {state.success && <p className="success">Post created!</p>}
42 </form>
43 );
44}React 19, officially released on December 5, 2024, represents a significant leap forward in web development. With automatic optimization through the React Compiler, the new Actions API, stable Server Components, and enhanced support for metadata, stylesheets, and scripts, building performant applications is easier than ever.
The elimination of forwardRef, automatic memoization, and streamlined form handling address long-standing pain points in React development. Combined with modern frameworks like Next.js 15 and powerful headless CMS solutions like Strapi, React 19 provides everything you need to build exceptional web applications.
Leading companies like Meta, Netflix, and Airbnb continue to prove React's effectiveness at scale, and with React 19's improvements, the future of React development looks brighter than ever.
To learn how to integrate React and Strapi, check out the integration page.