React 18 came out at the end of March with a bundle of new features. One of these new features is Transitions.

In this tutorial, you'll learn more about Transitions in React 18 and see them in action.

Definition

You have 2 types of transitions. Urgent transitions and non-urgent transitions.

Urgent Transitions

Urgent transitions are transitions that the user needs to see instantly. For example, if the user changes their profile name and saves it, they should be able to see the change in the displayed profile name in the navigation bar.

Urgent transitions are done the same way you've been setting a state before:

const [name, setName] = useState('');

function handleChange (e) {
	setName(e.target.value); //urgent transition
}

Non-Urgent Transitions

Non-urgent transitions are transitions that are ok to be delayed a little. For example, if the user is performing a search, it's ok for the results to appear not so instantly.

There are 2 ways to start a non-urgent transition. The first one is using the hook useTransition:

import {useTransition, useState} from 'react';

export default function MyApp() {
    const [results, setResults] = useState([]);
    const [pending, startTransition] = useTransition();
    
    function handleChange(e) {
        let tempResults = [];
        ... // set results from APIs
        startTransition(() => {
            setResults(tempResults);
        });
    }
}

The hook returns the boolean variable pending which indicates whether the transition is active or not. It can be used to show a loading component.

The hook also returns a function startTransition that accepts a callback function in which you set the state. The state will not be set immediately.

The second way to start a non-urgent transition is using the function startTransition imported directly from React:

import {startTransition} from 'react';

export default function MyApp() {
    const [results, setResults] = useState([]);
    
    function handleChange(e) {
        let tempResults = [];
        ... // set results from APIs
        startTransition(() => {
            setResults(tempResults);
        });
    }
}

This approach does not give you access to a variable like isPending to check whether the transition is active or not.

This approach is mainly available for places in your code when you can't use hooks like useTransition.

Usage Example

In this example, you'll be creating a number input that accepts a large number of images to be shown. Then, random images will be retrieved using Falso.

Start by creating a new React app if you don't have one available:

npx create-react-app my-app

Next, change into the directory my-app:

cd my-app

Then, install the Falso library:

npm i @ngneat/falso

Now, open src/App.js and change it to the following:

import './App.css';

import { useState, useTransition } from 'react';

import { randImg } from '@ngneat/falso';

function App() {
  const [number, setNumber] = useState(5000);
  const [images, setImages] = useState([])
  const [isPending, startTransition] = useTransition();

  function showImages() {
    //TODO add transition
  }

  return (
    <div style={{
      padding: '10px'
    }}>
      <h1>Images</h1>
      <div>
        <label>Number of images</label>
        <input type="number" min="1" max="10000" value={number} onChange={(e) => setNumber(e.target.value)} style={{
          display: 'block',
          marginTop: '10px',
          width: '3rem'
        }} />
        <button type="button" onClick={showImages} style={{marginTop: '10px'}}>Show Images</button>
      </div>
      <div>
        <span>Number selected: {number}</span><br/>
        <span>Results:</span>
        {isPending && <span>Loading...</span>}
        {!isPending && images.length > 0 && images}
      </div>
    </div>
  );
}

export default App;

You first create 2 state variables number and images. You also use useTransition which gives you access to isPending and startTransition.

In the returned JSX, you show a number input and a button that will later retrieve the images on click.

Below the input and button, the number entered by the user in the input will be shown. Notice that in the onChange event handler for the input the name is updated urgently. This will show the number instantly as it is updated by the user.

Let's test it out now. Run the website server:

npm start

This will open the website in your browser. If you try to update the input, you'll notice that the number displayed below it will update instantly.

0:00
/

Now, let's test the non-urgent transition. In showImages replace the TODO with the following code:

const imgSources = randImg({length: number}).map((url, index) => <img src={`${url}?v=${index}`} key={index} />);
startTransition(() => {
	setImages(imgSources);
})

This will get the images using the falso library and inside startTransition with set the images state variable.

Notice that in the returned JSX of the function we use isPending to indicate whether to show "Loading..." or not.

If you try clicking on the button now, the "Loading..." text will show first, and then the images will be shown gradually.

0:00
/

Conclusion

Transitions in React 18 allow you to optimize your user experience, especially for tasks or features that require some time to load. You can now use Transitions in React 18 to differentiate between instate updates and updates that can be delayed, and show in the UI any necessary loading more efficiently for those that require more time.