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 uses a component-based architecture that promotes code reusability and maintainability. It forms a hierarchical structure where data flows from parent to child components.
- The React Compiler (introduced in v19) automatically optimizes your components, eliminating the need for manual memoization in most cases.
- React's declarative approach simplifies development by letting you describe the desired UI state instead of the steps to achieve it.
- React 19 introduces Actions for handling data mutations, Server Components for improved performance, and enhanced support for document metadata, stylesheets, and async scripts.
Understanding the Core Concepts of React.js
React's strength comes from essential concepts working seamlessly:
- Declarative programming
- Component-based architecture
- JavaScript XML (JSX) syntax
- Automatic optimization via the React Compiler
- Interactive components and state management
- Actions for data mutations
- Server Components
These core principles make React powerful, distinctive, and developer-friendly.
Declarative Programming
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.
Component-Based Architecture
React encourages you to build UIs from small, reusable, independent components. Each component encapsulates its structure and behavior. This building-block approach lets developers:
- Reuse UI elements across different parts of an application
- Develop and test components in isolation
- Maintain a cleaner separation of concerns
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 Syntax Extension
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:
- Embed JavaScript expressions within curly braces ({})
- Map arrays directly to UI elements
- Conditionally render components based on state or props
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 Compiler: Automatic Optimization
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.
Interactive Components and State Management in React.js
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.
Using Props in React.js
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}Ref Cleanup Functions
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.
Managing State in React.js
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.
Actions: Handling Data Mutations
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}useFormStatus Hook
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}Optimistic Updates
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}Advanced React.js Concepts
As your React applications grow, you'll need more advanced techniques to effectively manage resources, state, and navigation.
The use() Hook
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}Modern Hooks Overview
React 19 includes all existing hooks plus new additions:
Core Hooks:
useState: Manage component stateuseEffect: Handle side effectsuseRef: Create mutable referencesuseTransition: Mark state updates as transitions
New React 19 Hooks:
use(): Read Promises and Context conditionallyuseActionState: Manage form submission stateuseOptimistic: Implement optimistic updatesuseFormStatus: Access form pending state
Additional Hooks:
useDeferredValue: Defer non-urgent updates (now supports initial values)useReducer: Manage complex stateuseMemoanduseCallback: Performance optimizations (less needed with React Compiler)
Context API Improvements
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}Document Metadata
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.
Stylesheet and Script Support
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.
Resource Preloading
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}Server Components and Server Actions
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.
Custom Elements Support
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.
Improved Error Handling
React 19 improves error handling with:
- Better hydration errors: Shows diffs between server and client HTML
- Reduced duplicate errors: Single error log instead of multiple
- New error callbacks:
onCaughtError,onUncaughtError,onRecoverableError
1const 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});Advantages of Using React.js
React has gained widespread adoption due to its clear benefits:
- Automatic Performance Optimization: React 19's compiler automatically optimizes your components, eliminating manual memoization needs and ensuring peak performance.
- Component Reusability: Build once, use everywhere. Companies like Airbnb maintain component libraries that reduce bugs and ensure UI consistency.
- Strong Community & Ecosystem: With over 10 million weekly npm downloads and frameworks like Next.js and Remix, you're supported by an active community.
- Simplified Data Mutations: The Actions API streamlines form handling and asynchronous operations with built-in pending states and error handling.
- Server-Side Rendering: Server Components enable faster initial loads and better SEO by rendering on the server.
- Cross-Platform Development: Use React Native to build mobile apps with the same React skills.
- Predictable Data Flow: React's unidirectional data flow makes applications easier to debug and maintain.
How to Install React.js
Before you start building with React, you'll need Node.js (version 18 or later) and npm installed on your system.
Setting Up a React.js Environment
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 devBuilding a Simple React.js Component
Let'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.js with a Headless CMS like Strapi
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}Maximize Your React.js Projects
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.