Tell us about the useTransition hook, how it works, where it's applied and for what purpose?

👨‍💻 Frontend Developer 🟡 Often Asked 🎚️ Medium
#React #Hooks

Brief Answer

useTransition is a React hook that allows you to mark state updates as transitions, which helps improve user experience by keeping the UI responsive during expensive operations. It separates urgent updates (like typing) from non-urgent updates (like filtering a list).

Syntax: const [isPending, startTransition] = useTransition();

Key features:

  • Improves UI responsiveness during expensive operations
  • Separates urgent from non-urgent state updates
  • Provides visual feedback with isPending state
  • Works with concurrent rendering features

Usage example:

function SearchComponent() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    setQuery(value);
    
    startTransition(() => {
      // Expensive filtering operation
      setSearchResults(filterData(value));
    });
  };
  
  return (
    <div>
      <input onChange={handleSearch} value={query} />
      {isPending && <div>Loading...</div>}
    </div>
  );
}

Full Answer

The useTransition hook is one of React’s built-in hooks designed to improve user experience by marking state updates as transitions. It helps keep the UI responsive during expensive operations by separating urgent updates (like user input) from non-urgent updates (like filtering or sorting data). 🚀

How useTransition works?

useTransition returns an array with two values:

  1. isPending — boolean indicating if transition is active
  2. startTransition — function to wrap non-urgent state updates
import React, { useState, useTransition } from 'react';
 
function Component() {
  const [isPending, startTransition] = useTransition();
  const [data, setData] = useState([]);
  
  const handleFilter = (filterValue) => {
    // Urgent update - immediate feedback
    // setFilter(filterValue);
    
    // Non-urgent update - wrapped in transition
    startTransition(() => {
      setData(filterExpensiveData(filterValue));
    });
  };
  
  return (
    <div>
      {isPending ? <div>Updating...</div> : <DataList data={data} />}
    </div>
  );
}

When startTransition is called:

  1. React marks the update as non-urgent
  2. Urgent updates continue to respond immediately
  3. Non-urgent updates are processed in background
  4. isPending becomes true during processing

Main useTransition applications

1. Improving search experience

function SearchableList({ items }) {
  const [query, setQuery] = useState('');
  const [filteredItems, setFilteredItems] = useState(items);
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    setQuery(value);
    
    // Keep typing responsive while filtering happens in background
    startTransition(() => {
      setFilteredItems(
        items.filter(item => 
          item.name.toLowerCase().includes(value.toLowerCase())
        )
      );
    });
  };
  
  return (
    <div>
      <input 
        value={query} 
        onChange={handleSearch} 
        placeholder="Search items..."
      />
      {isPending && <div>Searching...</div>}
      <ItemList items={filteredItems} />
    </div>
  );
}

2. Tab switching with expensive content

function TabComponent() {
  const [tab, setTab] = useState('home');
  const [isPending, startTransition] = useTransition();
  
  const switchTab = (newTab) => {
    // Immediate visual feedback
    startTransition(() => {
      // Expensive tab content rendering
      setTab(newTab);
    });
  };
  
  return (
    <div>
      <nav>
        <button onClick={() => switchTab('home')}>Home</button>
        <button onClick={() => switchTab('profile')}>Profile</button>
        <button onClick={() => switchTab('settings')}>Settings</button>
      </nav>
      
      {isPending && <div>Loading tab...</div>}
      <TabContent tab={tab} />
    </div>
  );
}

3. Filtering large datasets

function DataFilter({ largeDataset }) {
  const [filter, setFilter] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const [filteredData, setFilteredData] = useState(largeDataset);
  
  const applyFilter = (newFilter) => {
    setFilter(newFilter);
    
    startTransition(() => {
      const result = largeDataset.filter(item => 
        item.category.includes(newFilter)
      );
      setFilteredData(result);
    });
  };
  
  return (
    <div>
      <input 
        value={filter} 
        onChange={(e) => applyFilter(e.target.value)} 
        placeholder="Filter by category..."
      />
      {isPending ? <div>Filtering...</div> : <DataGrid data={filteredData} />}
    </div>
  );
}

When to use useTransition

Use useTransition when:

  1. Expensive state updates — filtering, sorting large datasets
  2. User input handling — keeping typing responsive during operations
  3. Tab or view switching — when content rendering is expensive
  4. Search operations — maintaining responsiveness during filtering
function ExpensiveOperationComponent({ data }) {
  const [searchTerm, setSearchTerm] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (term) => {
    setSearchTerm(term);
    
    // Keep UI responsive while searching
    startTransition(() => {
      performExpensiveSearch(data, term);
    });
  };
  
  return (
    <div>
      <SearchInput onSearch={handleSearch} />
      {isPending && <LoadingIndicator />}
      <Results />
    </div>
  );
}

When to avoid useTransition

Avoid useTransition when:

  1. Simple state updates — basic form inputs, simple toggles
  2. Immediate feedback required — when users need instant results
  3. Small datasets — for small lists or quick operations
  4. When performance is not critical — premature optimization
// ❌ Don't use useTransition unnecessarily
function SimpleToggle() {
  const [isVisible, setIsVisible] = useState(true);
  const [, startTransition] = useTransition(); // Overkill!
  
  const toggle = () => {
    startTransition(() => {
      setIsVisible(!isVisible);
    });
  };
  
  return <button onClick={toggle}>{isVisible ? 'Hide' : 'Show'}</button>;
}
 
// ✅ Just use regular state update
function SimpleToggle() {
  const [isVisible, setIsVisible] = useState(true);
  
  const toggle = () => {
    setIsVisible(!isVisible);
  };
  
  return <button onClick={toggle}>{isVisible ? 'Hide' : 'Show'}</button>;
}

Common mistakes

1. Using useTransition unnecessarily

// ❌ Overuse
function Component() {
  const [count, setCount] = useState(0);
  const [, startTransition] = useTransition();
  
  const increment = () => {
    startTransition(() => {
      setCount(c => c + 1); // Simple increment doesn't need transition
    });
  };
  
  return <button onClick={increment}>Count: {count}</button>;
}
 
// ✅ Just use regular state update
function Component() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    setCount(c => c + 1);
  };
  
  return <button onClick={increment}>Count: {count}</button>;
}

2. Wrapping urgent updates in transitions

// ❌ Error: wrapping urgent updates
function SearchComponent() {
  const [query, setQuery] = useState('');
  const [, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    
    startTransition(() => {
      setQuery(value); // Should be immediate for typing feedback
    });
  };
  
  return <input value={query} onChange={handleSearch} />;
}
 
// ✅ Correctly: separate urgent and non-urgent updates
function SearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    
    // Urgent update for immediate typing feedback
    setQuery(value);
    
    // Non-urgent update for expensive filtering
    startTransition(() => {
      setResults(filterResults(value));
    });
  };
  
  return <input value={query} onChange={handleSearch} />;
}

3. Not providing user feedback

// ❌ No feedback during transition
function Component() {
  const [data, setData] = useState([]);
  const [isPending, startTransition] = useTransition();
  
  const loadData = () => {
    startTransition(() => {
      setData(fetchExpensiveData());
    });
  };
  
  return (
    <div>
      <button onClick={loadData}>Load Data</button>
      {/* User doesn't know operation is in progress */}
      <DataDisplay data={data} />
    </div>
  );
}
 
// ✅ Provide visual feedback
function Component() {
  const [data, setData] = useState([]);
  const [isPending, startTransition] = useTransition();
  
  const loadData = () => {
    startTransition(() => {
      setData(fetchExpensiveData());
    });
  };
  
  return (
    <div>
      <button onClick={loadData}>Load Data</button>
      {isPending && <div className="loading">Loading...</div>}
      <DataDisplay data={data} />
    </div>
  );
}

Summary

useTransition is a React hook for:

  • Improving UI responsiveness during expensive operations
  • Separating urgent updates from non-urgent updates
  • Providing better user experience with visual feedback
  • Working with concurrent rendering features

When to use:

  • Expensive state updates (filtering, sorting large datasets)
  • User input handling (keeping typing responsive)
  • Tab or view switching with expensive content
  • Search operations that require filtering

When to avoid:

  • Simple state updates (basic toggles, form inputs)
  • When immediate feedback is required
  • Small datasets or quick operations
  • When performance optimization is not needed

Best practices:

  • Use only when you have expensive operations
  • Always provide visual feedback with isPending
  • Separate urgent updates from non-urgent updates
  • Don’t use for simple state changes

useTransition is a powerful tool for improving user experience, but it should be used thoughtfully. Identify real performance bottlenecks and apply useTransition to solve them. 🚀


Want more articles to prepare for interviews? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪