Waiting for server confirmations before updating the UI feels sluggish. In real-world apps, users expect near-instant feedback. Enter optimistic updates: immediately reflect changes in the UI while the server catches up. Redux Toolkit makes this surprisingly clean — no extra libraries needed.

What Are Optimistic Updates?

Instead of waiting for a network response, you assume the action will succeed and update the UI instantly. If the server later responds with an error, you roll back or show a correction.

Step 1: Structure the Slice for Pending States

Let's model an optimistic update for a "like" feature:

// src/features/posts/postsSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

export const likePost = createAsyncThunk(
  'posts/likePost',
  async (postId) => {
    await axios.post(`/api/posts/${postId}/like`);
    return postId;
  }
);

const postsSlice = createSlice({
  name: 'posts',
  initialState: {
    posts: [],
    error: null,
  },
  reducers: {
    optimisticLike(state, action) {
      const post = state.posts.find(p => p.id === action.payload);
      if (post) {
        post.likes++;
      }
    },
    revertLike(state, action) {
      const post = state.posts.find(p => p.id === action.payload);
      if (post) {
        post.likes--;
      }
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(likePost.fulfilled, (state, action) => {
        // No-op because UI already updated optimistically
      })
      .addCase(likePost.rejected, (state, action) => {
        state.error = 'Failed to like post';
      });
  },
});

export const { optimisticLike, revertLike } = postsSlice.actions;
export default postsSlice.reducer;

Step 2: Dispatch in the Right Order

In your React component:

// src/features/posts/LikeButton.js
import { useDispatch } from 'react-redux';
import { optimisticLike, revertLike, likePost } from './postsSlice';

function LikeButton({ postId }) {
  const dispatch = useDispatch();

  const handleLike = () => {
    dispatch(optimisticLike(postId));
    dispatch(likePost(postId)).unwrap().catch(() => {
      dispatch(revertLike(postId));
    });
  };

  return (
    
  );
}

How It Works

  • First, optimisticLike immediately increments likes in the local state.
  • Then, likePost sends the actual API request.
  • If the request fails, revertLike corrects the UI by decrementing.

Pros and Cons

✅ Pros

  • Instant feedback for users
  • Very little code overhead with Redux Toolkit
  • Decouples UI responsiveness from server performance

⚠️ Cons

  • Edge cases if the server state diverges (e.g., data race conditions)
  • Rollback logic can become complex for more critical operations

🚀 Alternatives

  • RTK Query: Handles optimistic updates natively if you use RTKQ for data fetching
  • TanStack Query: (non-Redux) has built-in support for optimistic mutations

Summary

Optimistic updates aren't just for fancy apps — they're fundamental for great UX. With Redux Toolkit’s built-in capabilities, you can implement this advanced pattern cleanly without needing external dependencies.

If you found this useful, you can support me here: buymeacoffee.com/hexshift