Optimistic UI Updates
In this page:
function handleAction() {
const previous = state;
setState(optimisticValue); // update the UI immediately
sendToServer().catch(() => setState(previous)); // revert on failure
}
Updating the UI Before the Server Confirms
In a normal flow, you'd wait for a server response before updating the UI, which can feel sluggish for small, usually-successful actions. An optimistic update flips this: change the local state immediately, assuming success, then quietly fire off the real request in the background.
Example: Updating the UI Before the Server Confirms
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [liked, setLiked] = React.useState(false);
const handleLike = () => {
setLiked(true); // update immediately
// fetch('/api/like', { method: 'POST' }); // fires in background
};
return <button onClick={handleLike}>{liked ? "Liked!" : "Like"}</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Reverting on Failure
Since the optimistic update assumes success, you need a plan for when the real request fails: storing the previous value before changing it lets you roll the UI back to that exact prior state if the server responds with an error.
Example: Reverting on Failure
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [liked, setLiked] = React.useState(false);
const handleLike = () => {
const previous = liked;
setLiked(true);
const requestFailed = true; // simulate a failed request
if (requestFailed) setLiked(previous);
};
return <button onClick={handleLike}>{liked ? "Liked!" : "Like (will revert)"}</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Optimistic Updates for Lists
The same idea extends to adding or removing items from a list: add the new item to local state immediately, send the real request in the background, and remove it again (or show an error) if the request ultimately fails.
Example: Optimistic Updates for Lists
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [items, setItems] = React.useState(["First item"]);
const addItem = () => {
setItems(prev => [...prev, "New item (optimistic)"]);
// fetch('/api/items', { method: 'POST' }).catch(() => {
// setItems(prev => prev.slice(0, -1)); // revert on failure
// });
};
return <div><button onClick={addItem}>Add Item</button><ul>{items.map((i, idx) => <li key={idx}>{i}</li>)}</ul></div>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Not reverting the optimistic change if the actual server request fails, leaving the UI permanently showing incorrect data.
- Applying the optimistic update after the request instead of before, defeating the whole point of feeling instant.
- Not giving any visual cue that an update is pending confirmation, if that matters for the use case.
- An optimistic update changes the UI immediately, assuming a request will succeed, before waiting for the server's response.
- This makes actions like liking a post or checking off a to-do feel instant rather than sluggish.
- If the actual request fails, the UI must be rolled back to its previous state.
- Storing the previous value before updating makes reverting straightforward if needed.
No React-version restriction — a UI pattern built from useState and standard request handling.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: