The useOptimistic hook
This hook lets you update the UI immediately in response to an action, before the server responds.
You pass to it the current state you want to manage through it (for example, an array messages
), and a function to update the optimistic state.
It returns you the optimistic state (which you use for immediate rendering), and a function to update it.
You call the hook before the server request.
After the server response is received, you update the actual state (in the example, messages
, and in the TSX you render the optimistic state, which after you update with the actual state, renders the actual state.
You can use this hook in a client component.
Here’s a simple todo app example.
Define an addTodo
server action:
'use server'
type Todo = {
todo: string
}
export async function addTodo(newTodo: string): Promise<Todo> {
// Simulating server delay
await new Promise((resolve) => setTimeout(resolve, 3000))
// Add todo on server
return {
todo: newTodo + ' test',
}
}
In this server action, after 3 seconds we return the todo sent, plus ' test'
to understand that this is the returned value from the server action.
Client-side we use the useOptimistic
hook to generate an optimisticTodos
array, which we use to list todos in the TSX:
const [optimisticTodos, addOptimisticTodo] = useOptimistic<Todo[], string>(
todos,
(state, newTodo) => [...state, { todo: newTodo }]
)
When we hit the form action (the user presses the submit button), the addOptimisticTodo()
function is called to add the new todo.
Then we hit the server, which takes some time as the server action waits 3 seconds).
Finally when we’re back we call setTodos()
to update the todos list with the actual state coming from the server.
Full code:
'use client'
import { useOptimistic, useState, useRef } from 'react'
import { addTodo } from './actions'
type Todo = {
todo: string
}
export default function Todos() {
const [todos, setTodos] = useState<Todo[]>([])
const formRef = useRef<HTMLFormElement>(null)
const [optimisticTodos, addOptimisticTodo] = useOptimistic<Todo[], string>(
todos,
(state, newTodo) => [...state, { todo: newTodo }]
)
const formAction = async (formData: FormData) => {
const todo = formData.get('todo') as string
addOptimisticTodo(todo)
formRef.current?.reset()
try {
const result = await addTodo(todo)
// Update the actual state with the server response
setTodos((prevTodos) => [...prevTodos, { todo: result.todo }])
} catch (error) {
console.error('Error adding todo:', error)
// Optionally, you could remove the optimistic update here if the server request fails
}
}
return (
<div>
{optimisticTodos.map((m, i) => (
<div key={i}>{m.todo}</div>
))}
<form action={formAction} ref={formRef}>
<input type='text' name='todo' />
<button type='submit'>Send</button>
</form>
</div>
)
}
→ I wrote 17 books to help you become a better developer:
- C Handbook
- Command Line Handbook
- CSS Handbook
- Express Handbook
- Git Cheat Sheet
- Go Handbook
- HTML Handbook
- JS Handbook
- Laravel Handbook
- Next.js Handbook
- Node.js Handbook
- PHP Handbook
- Python Handbook
- React Handbook
- SQL Handbook
- Svelte Handbook
- Swift Handbook
Also, JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025