DEV Community

Cover image for React 19: New Features, Advanced Techniques, and Enhanced Usage Methods 🚀
Abdulnasır Olcan
Abdulnasır Olcan

Posted on

React 19: New Features, Advanced Techniques, and Enhanced Usage Methods 🚀

React 19 brings numerous innovations to make modern web development more efficient, faster, and user-friendly. Key improvements include server-side component processing, advanced data loading methods, streamlined form management, and flexible ref usage. These changes mark a significant step forward for React 19. This article delves into each new feature with in-depth analysis and advanced examples.

Key Features:

  • React Compiler: Transforms React code into optimized JavaScript, potentially doubling performance.
  • Actions: Simplifies data updates, especially for forms.
  • Server Components: Enables faster loading and improved SEO with server-side rendering.
  • Enhanced Hooks: Offers more control over component lifecycles.
  • Asset Loading: Preloads resources for smoother transitions.
  • Web Components: Enhances compatibility and flexibility in development.

1. Server Components: Performance and SEO Improvements

One of React 19’s most notable features is server-side component processing. Server Components work on the server, sending pre-rendered HTML to the client, drastically reducing load times and enhancing SEO.

// MyComponent.server.js
export default function MyComponent() {
  const data = fetch('https://api.example.com/data').then(res => res.json());
  return <div>{data.title}</div>;
}
Enter fullscreen mode Exit fullscreen mode

In this example, MyComponent is processed on the server and sent as pre-rendered HTML to the browser. This allows users to load the page more quickly while maintaining data security on the server side.

2. Actions API: Form Management and Action-Based Requests

The Actions API is used to manage data updates between the client and server. It simplifies handling form interactions and provides quick responses to user actions.

Example:

import { createAction } from 'react';

const updateUser = createAction(async (data) => {
  await fetch('/api/update-user', {
    method: 'POST',
    body: JSON.stringify(data),
  });
});

function UserProfile() {
  return (
    <form onSubmit={updateUser}>
      <input name="username" placeholder="Username" />
      <button type="submit">Update</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example, the updateUser function sends data directly to the server, allowing the form submission to be completed quickly.

3. use() Hook: Data Fetching and Context Management

React 19 introduced the use() hook to simplify data management. This hook allows us to work with context values or asynchronous operations in a cleaner and more straightforward way.

Example:

// context.js
export const UserContext = createContext();

// App.js
import { use, UserContext } from './context';

function UserProfile() {
  const user = use(UserContext);
  return <div>{user.name}</div>;
}
Enter fullscreen mode Exit fullscreen mode

This usage makes the code more readable, especially in context-based data management.

Using the use() Hook for Data Fetching Without Creating Context

Let's create an example that uses the use() hook for working with asynchronous data without needing to set up a context. This example demonstrates using use() to work with an async data source:

Example:

import { use } from 'react';

// Fake async data fetching function
async function fetchUser() {
  const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
  return response.json();
}

// `use()` Hook directly utilizes the async data
function UserProfile() {
  const user = use(fetchUser());

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
      <p>Website: {user.website}</p>
    </div>
  );
}

export default UserProfile;

Enter fullscreen mode Exit fullscreen mode

fetchUser Function: Asynchronously retrieves user data from the API.

use(fetchUser()): The use() hook uses the data returned by the async function, managing the loading process automatically.

Data Usage: When the user data is loaded, it is used directly within the component to display the user's details on the screen.

4. Prefetching and Resource Loading: Key Improvements for User Experience

Methods like prefetchDNS, preconnect, preload, and preinit in the react-dom library speed up access to network and data resources.

Example:

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

prefetchDNS('https://example.com');
preconnect('https://api.example.com');
preload('/assets/image.png', { as: 'image' });
preinit('/scripts/library.js', { as: 'script' });
Enter fullscreen mode Exit fullscreen mode

With these methods, resources that users may need are preloaded, offering a smoother experience.

a. Async Scripts Support
React 19 manages scripts marked as async more effectively. This is particularly beneficial for loading third-party libraries or external APIs faster.

Example:

import { preinit } from 'react-dom';

preinit('https://example.com/library.js', { as: 'script', async: true });
Enter fullscreen mode Exit fullscreen mode

With this method, the specified script is loaded asynchronously, enhancing the application's performance.

b. Stylesheet Support
React 19 allows more efficient loading of stylesheets using preload and preconnect. Stylesheets can be preloaded in the background before the user accesses the page, improving the overall loading experience.

Example:

import { preload } from 'react-dom';

preload('/styles/main.css', { as: 'style' });
Enter fullscreen mode Exit fullscreen mode

This ensures that the main.css file is ready before the page loads, allowing for a faster initial render.

5. New useFormStatus and useFormState Hooks for Form Management

React 19 introduces new hooks for managing form states and data. With useFormStatus() and useFormState(), tracking form statuses and user inputs becomes easier and more efficient.

Example:

import { useFormStatus, useFormState } from 'react';

function ContactForm() {
  const { isSubmitting } = useFormStatus();
  const { values } = useFormState();

  return (
    <form>
      <input name="email" placeholder="Email" />
      <button disabled={isSubmitting}>Send</button>
      {values.email && <p>Email Address: {values.email}</p>}
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

These hooks make form submission processes smoother and improve the user experience.

6. ref Callback Usage: Managing Refs in Parent-Child Relationships

In React 19, ref callbacks replace forwardRef, providing more flexible communication between parent and child components.

Example:

import { useRef, useEffect } from 'react';

function ChildInput({ innerRef }) {
  return (
    <input
      ref={(ref) => {
        innerRef(ref);
        if (ref) {
          console.log('Child Input ref created:', ref);
        }
      }}
      placeholder="Text Input..."
    />
  );
}

function ParentComponent() {
  const inputRef = useRef();

  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
      console.log('Parent focuses the child input');
    }

    return () => {
      console.log('Cleaning up parent reference');
    };
  }, []);

  return (
    <div>
      <h1>Parent-Child Ref Example</h1>
      <ChildInput innerRef={(ref) => (inputRef.current = ref)} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example, the parent component can access the input element within the child component and perform actions on it.

7. Using Suspense for Concurrent Rendering

React 19 introduces concurrent rendering to optimize the processing of components. This feature allows users to interact with the interface more quickly and efficiently. It manages the rendering of components in a way that prioritizes responsiveness, making the application feel smoother and more interactive.

Example:

import { Suspense } from 'react';

function DataComponentFirst() {
  const data = fetch('/api/data/first').then(res => res.json());
  return <div>{data}</div>;
}

function DataComponentSecond() {
  const data = fetch('/api/data/second').then(res => res.json());
  return <div>{data}</div>;
}

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <DataComponentFirst />
      <DataComponentSecond />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

Concurrent Rendering: DataComponentFirst and DataComponentSecond start loading simultaneously, but the one whose data is ready first is displayed first.

Asynchronous Processing: React displays the component that completes its data load while the other continues to load. For example, if DataComponentFirst loads first, it appears on the screen, and DataComponentSecond follows once it’s ready.

In Summary: This way, data is shown to the user as it becomes available without waiting for other components, resulting in a faster and more user-friendly data loading process.

b. Multi-Data Loading Management

Suspense can now be nested for multiple data sources, making data loading processes more flexible.

Example:

import { Suspense } from 'react';

function DataComponent1() {
  const data1 = fetchDataFromAPI1();
  return <div>{data1}</div>;
}

function DataComponent2() {
  const data2 = fetchDataFromAPI2();
  return <div>{data2}</div>;
}

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading... (1)</div>}>
        <DataComponent1 />
        <Suspense fallback={<div>Loading... (2)</div>}>
          <DataComponent2 />
        </Suspense>
      </Suspense>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This example provides a better user experience by managing the data loading processes efficiently.

8. Automated Code Splitting: Automatic Code Partitioning

React 19 enhances performance by allowing applications to load only the necessary code as needed, reducing the initial bundle size and improving loading times.

Example:

const LazyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

This usage improves performance by loading large components only when needed.

9. Document Metadata Management

React 19 provides enhanced support for managing <title>, <meta>, and <link> elements, making it easier to handle SEO optimization and page titles.

Example:

import { DocumentHead } from 'react';

function SEOPage() {
  return (
    <DocumentHead>
      <title>Custom Title - React 19</title>
      <meta name="description" content="Learn about the new features of React 19." />
    </DocumentHead>
  );
}
Enter fullscreen mode Exit fullscreen mode

10. Stylesheet Management

React 19 optimizes the loading of CSS and other style files, providing a smoother experience during page transitions. It improves the management of tags, ensuring that styles are applied more efficiently.

Example:

import './styles.css';

function StyledComponent() {
  return <div className="styled">This component uses a stylesheet.</div>;
}
Enter fullscreen mode Exit fullscreen mode

11. useSyncExternalStore (Synchronization with External Data Sources)

React 19 introduces the useSyncExternalStore hook for subscribing to external data sources. This enables more efficient handling of global data in applications.

Example:

import { useSyncExternalStore } from 'react';

function useWindowSize() {
  return useSyncExternalStore(
    (onChange) => window.addEventListener('resize', onChange),
    () => ({ width: window.innerWidth, height: window.innerHeight })
  );
}

function WindowSizeComponent() {
  const size = useWindowSize();
  return <div>Window Size: {size.width} x {size.height}</div>;
}
Enter fullscreen mode Exit fullscreen mode

This example creates a component that responds to window size changes.

12. Directives

Directives like 'use client' and 'use server' enforce whether components should run on the client or server side. This makes it clear where a component should be rendered, helping to manage behavior and performance more effectively.

Example:

'use client';

export default function ClientOnlyComponent() {
  return <div>Runs on the client only</div>;
}

Enter fullscreen mode Exit fullscreen mode

13. Automated Code Splitting (Otomatic Code Splitting)

React 19 accelerates page load times by automating component-based code splitting. It ensures that only the necessary code is loaded when required, optimizing performance and reducing initial bundle sizes.

Example:

const LazyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

14. Asset Loading

React 19 makes it easier to load assets like images and videos in the background. By preloading these assets during page transitions, it improves the user experience by reducing waiting times and ensuring smoother navigation.

Example:

const MyLazyImage = React.lazy(() => import('./MyImage'));

function Gallery() {
  return (
    <Suspense fallback={<div>Image Loading...</div>}>
      <MyLazyImage />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

15. Web Components Support

Web Components have become more compatible with React components in React 19. This improvement makes it easier to integrate components developed with different frameworks into React applications, enhancing interoperability and flexibility.

Example:

class MyCustomElement extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<p>React with Web Component</p>`;
  }
}
customElements.define('my-custom-element', MyCustomElement);

function App() {
  return <my-custom-element></my-custom-element>;
}
Enter fullscreen mode Exit fullscreen mode

React 19 allows these kinds of custom components to work seamlessly.

16. useOptimistic New Hook

The useOptimistic hook introduced in React 19 is designed to make the user interface smoother. It is particularly useful for making optimistic updates for actions sent to the server, allowing changes to be reflected in the UI without delays, even before the server's response.

Example:

import { useOptimistic } from 'react';

function LikeButton({ initialLikes }) {
  const [likes, updateLikes] = useOptimistic(initialLikes, (currentLikes) => currentLikes + 1);

  return (
    <button onClick={() => updateLikes()}>
      {likes} Like
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

useOptimistic: Takes an initial value as the first parameter (e.g., initialLikes).

Optimistic Update: When updateLikes is called, the number of likes is immediately updated in the UI.

UI Responsiveness: Updates are displayed optimistically before the server response, providing a smoother user experience.

This way, UI changes can be made instantly without waiting for the process to complete, enhancing user interactions.

Advanced Example: A more complex use of useOptimistic can include managing optimistic updates while handling errors and rollbacks during data submission to the server, balancing responsiveness and reliability.

Example:

import { useState, useOptimistic } from 'react';

async function updateServerLikes(likes) {
  const response = await fetch('/api/update-likes', {
    method: 'POST',
    body: JSON.stringify({ likes }),
  });
  if (!response.ok) {
    throw new Error('Failed to update likes');
  }
}

function LikeButton({ initialLikes }) {
  const [error, setError] = useState(null);
  const [likes, updateLikes] = useOptimistic(
    initialLikes,
    (currentLikes, setOptimisticValue) => {
      // Optimistically increase the new value
      setOptimisticValue(currentLikes + 1);

      // Send the update to the server
      updateServerLikes(currentLikes + 1).catch((err) => {
        // Roll back the optimistic value in case of an error
        setOptimisticValue(currentLikes);
        setError('Server error: Update failed');
      });
    }
  );

  return (
    <div>
      <button onClick={() => updateLikes()}>Like ({likes})</button>
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Error Handling: The updateServerLikes asynchronous function sends the new number of likes to the server. If the update fails, it throws an error.

Optimistic Update: Using useOptimistic with setOptimisticValue, the number of likes is immediately updated in the UI without waiting for the server response.

Rollback on Error: If updateServerLikes fails, the incremented likes count is rolled back, and an error message is shown to the user.

Smooth User Experience: Users see the updated likes instantly while the request is sent to the server.

Reliability: If the server update fails, useOptimistic rolls back the change to prevent incorrect data display.

Visual Feedback: Users receive immediate feedback in case of errors, improving their understanding of interactions.

17. useDeferredValue with Initial Value

The useDeferredValue hook improves UI responsiveness during heavy computations. By setting an initial value, it allows for more controlled transitions to deferred updates.

Example:

import { useState, useDeferredValue } from 'react';

function SearchComponent({ searchTerm }) {
  const deferredSearchTerm = useDeferredValue(searchTerm, {
    initialValue: ''
  });

  return <div>Searching for: {deferredSearchTerm}</div>;
}
Enter fullscreen mode Exit fullscreen mode

In this example, deferredSearchTerm only updates with a delay when the searchTerm value changes. This reduces latency while users type, creating a smoother interface.

These features further strengthen React 19's focus on performance and user experience, especially when dealing with intensive data processing or external resources.

Conclusion:

React 19 offers powerful and flexible tools for modern web development. Features like Server Components, Actions API, and useOptimistic() provide faster and more user-friendly experiences in web applications. Upgrading to React 19 is a great step toward improved performance and smoother user interactions.

Happy coding! 🚀

Top comments (0)