# A Guide to Using React 19's Latest Hook: useOptimistic

%%[kempo] 

React 19's `useOptimistic` hook lets you update your UI to its expected state after an asynchronous action *while* the action is still in progress.

Imagine you have an app that lets you add comments to some published content. When you add a new comment, a network request is made to the server to create the comment in the app's database.

This process takes time, so you need to account for this in your UI state. The flow might look something like this:

1. Make a `POST` request to create a new comment
    
2. Show a loading spinner in your UI
    
3. After the `POST` request finishes, make a `GET` request to fetch the latest list of comments, which should include your new comment
    
4. Remove the loading spinner and show the updated comments
    

### The useOptimisitic hook makes your UI feel snappier

Instead of showing a loading spinner and waiting for all your requests to resolve, you can use the `useOptimistic` hook to immediately add the comment. If the request fails, you can remove the comment and show an error message. The happy path feels much faster and it's easy to recover from the failure path.

The example below shows how this might work by combining the new `useActionState` hook with the `useOptimistic` hook.

> If you're unfamiliar with how the `useActionState` hook works, you can learn more in my post about [React 19 actions and form actions](https://blog.thesshguy.com/react-19-actions).

You'll notice we show a loading message when you submit a comment. But that's not the only thing we do. We *also* immediately show the comment in the comment section because we update the *optimistic* value. This optimistic value has an extra `unsaved` property that we use to style the comment differently.

Once the request finishes, we update the actual `comments` state which includes the newly created comment. This time, however, the comment *isn't* in an unsaved state, so it's styled normally.

To handle errors, we show a toast message and return the old list of comments. When the component re-renders, the `useOptimistic` hook will detect this, which will remove the optimistically added comment.

To try this out, you can pass the text `error` to the comment form.

<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3Vvd2YxdXc3aDdqZmk0Y2pxNWlhcXBrayIsIm5hbWUiOiJSZWFjdCAxOSBPcHRpbWlzdGljIFVwZGF0ZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIHBhZGRpbmc6IDJyZW07XG59XG5cbmgxIHtcbiAgZm9udC1zaXplOiAxLjVyZW07XG59XG5cbmgyIHtcbiAgZm9udC1zaXplOiAxLjI1cmVtO1xufVxuXG5mb3JtIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZ2FwOiAwLjVyZW07XG59XG5cbmZvcm0gYnV0dG9uIHtcbiAgYWxpZ24tc2VsZjogZmxleC1lbmQ7XG59XG5cbmxhYmVsIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgZ2FwOiAwLjEyNXJlbTtcbn1cblxuLnBvc3Qtd3JhcHBlcixcbi5jb21tZW50LXdyYXBwZXIge1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xufVxuXG4ucG9zdC13cmFwcGVyIGRpdiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBiYXNlbGluZTtcbiAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlQWN0aW9uU3RhdGUsIHVzZU9wdGltaXN0aWMgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IHVzZUZvcm1TdGF0dXMgfSBmcm9tIFwicmVhY3QtZG9tXCI7XG5pbXBvcnQgeyB0b2FzdCB9IGZyb20gXCJzb25uZXJcIjtcbmltcG9ydCB7IGNyZWF0ZUNvbW1lbnQsIFBvc3QsIENvbW1lbnRGb3JtLCBDb21tZW50cyB9IGZyb20gXCIuL3V0aWxzXCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpOiBKU1guRWxlbWVudCB7XG4gIGNvbnN0IFtjb21tZW50cywgc3VibWl0QWN0aW9uLCBpc1BlbmRpbmddID0gdXNlQWN0aW9uU3RhdGUoXG4gICAgYXN5bmMgKHByZXZDb21tZW50cywgZm9ybURhdGEpID0+IHtcbiAgICAgIGNvbnN0IGNvbW1lbnQgPSBmb3JtRGF0YS5nZXQoXCJjb21tZW50XCIpO1xuXG4gICAgICBzZXRPcHRpbWlzdGljQ29tbWVudHMoW1xuICAgICAgICAuLi5wcmV2Q29tbWVudHMsXG4gICAgICAgIHsgaWQ6IGBsb2NhbC0ke01hdGgucmFuZG9tKCl9YCwgY29tbWVudCwgdW5zYXZlZDogdHJ1ZSB9LFxuICAgICAgXSk7XG5cbiAgICAgIGNvbnN0IG5ld0NvbW1lbnQgPSBhd2FpdCBjcmVhdGVDb21tZW50KGNvbW1lbnQpO1xuXG4gICAgICBpZiAobmV3Q29tbWVudC5zdGF0dXMgPT09IFwiZXJyb3JcIikge1xuICAgICAgICB0b2FzdC5lcnJvcihcIkZhaWxlZCB0byBjcmVhdGUgY29tbWVudFwiKTtcbiAgICAgICAgcmV0dXJuIHByZXZDb21tZW50cztcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIFsuLi5wcmV2Q29tbWVudHMsIG5ld0NvbW1lbnQuY29tbWVudF07XG4gICAgfSxcbiAgICBbXSxcbiAgKTtcblxuICBjb25zdCBbb3B0aW1pc3RpY0NvbW1lbnRzLCBzZXRPcHRpbWlzdGljQ29tbWVudHNdID0gdXNlT3B0aW1pc3RpYyhjb21tZW50cyk7XG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPFBvc3QgaXNQZW5kaW5nPXtpc1BlbmRpbmd9IC8+XG4gICAgICA8Q29tbWVudEZvcm0gc3VibWl0QWN0aW9uPXtzdWJtaXRBY3Rpb259IGlzUGVuZGluZz17aXNQZW5kaW5nfSAvPlxuICAgICAgPENvbW1lbnRzIGNvbW1lbnRzPXtvcHRpbWlzdGljQ29tbWVudHN9IC8+XG4gICAgPC8+XG4gICk7XG59XG4ifSwiL2luZGV4LnRzeCI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IHsgVG9hc3RlciB9IGZyb20gXCJzb25uZXJcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxUb2FzdGVyIHBvc2l0aW9uPVwidG9wLXJpZ2h0XCIgLz5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT4sXG4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIixcbiAgICBcInNvbm5lclwiOiBcIjEuNC40MVwiXG4gIH0sXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcIkB0eXBlcy9yZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcIkB0eXBlcy9yZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJ0eXBlc2NyaXB0XCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LnRzeFwiXG59In0sIi91dGlscy50c3giOnsiY29kZSI6ImltcG9ydCB7IHRvYXN0IH0gZnJvbSBcInNvbm5lclwiO1xuXG5hc3luYyBmdW5jdGlvbiBzbGVlcCh0aW1lb3V0OiBudW1iZXIpIHtcbiAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlKChyKSA9PiBzZXRUaW1lb3V0KHIsIHRpbWVvdXQpKTtcbn1cblxubGV0IGNvbW1lbnRJZCA9IDE7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVDb21tZW50KGNvbW1lbnQ6IHN0cmluZykge1xuICBhd2FpdCBzbGVlcCgxMDAwKTtcblxuICBpZiAoY29tbWVudCA9PT0gXCJlcnJvclwiKSB7XG4gICAgcmV0dXJuIHsgc3RhdHVzOiBcImVycm9yXCIgfTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgc3RhdHVzOiBcInN1Y2Nlc3NcIixcbiAgICBjb21tZW50OiB7IGlkOiBjb21tZW50SWQrKywgY29tbWVudDogY29tbWVudCB8fCBcIkVtcHR5IENvbW1lbnRcIiB9LFxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gUG9zdCh7IGlzUGVuZGluZyB9KSB7XG4gIHJldHVybiAoXG4gICAgPGRpdiBjbGFzc05hbWU9XCJwb3N0LXdyYXBwZXJcIj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxoMT5NeSBQb3N0PC9oMT5cbiAgICAgICAge2lzUGVuZGluZyAmJiA8c3Bhbj5Mb2FkaW5nLi4uPC9zcGFuPn1cbiAgICAgIDwvZGl2PlxuICAgICAgPHA+VGhpcyBpcyBteSBwb3N0IGNvbnRlbnQ8L3A+XG4gICAgPC9kaXY+XG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBDb21tZW50cyh7IGNvbW1lbnRzIH0pIHtcbiAgcmV0dXJuIChcbiAgICA8ZGl2IGNsYXNzTmFtZT1cImNvbW1lbnRzLXdyYXBwZXJcIj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxoMj5Db21tZW50czwvaDI+XG4gICAgICA8L2Rpdj5cbiAgICAgIHtjb21tZW50cy5tYXAoKGNvbW1lbnQpID0+IChcbiAgICAgICAgPHAga2V5PXtjb21tZW50LmlkfSBzdHlsZT17eyBvcGFjaXR5OiBjb21tZW50LnVuc2F2ZWQgPyAwLjUgOiAxIH19PlxuICAgICAgICAgIHtjb21tZW50LmNvbW1lbnR9IHtjb21tZW50LnVuc2F2ZWQgPyBcIihzYXZpbmcuLi4pXCIgOiBcIlwifVxuICAgICAgICA8L3A+XG4gICAgICApKX1cbiAgICA8L2Rpdj5cbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENvbW1lbnRGb3JtKHsgc3VibWl0QWN0aW9uLCBpc1BlbmRpbmcgfSkge1xuICByZXR1cm4gKFxuICAgIDxmb3JtIGFjdGlvbj17c3VibWl0QWN0aW9ufT5cbiAgICAgIDxsYWJlbD5cbiAgICAgICAgPHNwYW4+Q29tbWVudDwvc3Bhbj5cbiAgICAgICAgPGlucHV0IG5hbWU9XCJjb21tZW50XCIgLz5cbiAgICAgIDwvbGFiZWw+XG4gICAgICA8YnV0dG9uIHR5cGU9XCJzdWJtaXRcIiBkaXNhYmxlZD17aXNQZW5kaW5nfT5cbiAgICAgICAgQWRkIENvbW1lbnRcbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZm9ybT5cbiAgKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5Ijp0cnVlLCJzaG93UmVhZE9ubHkiOnRydWV9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLnRzeCJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

And that's it! Hopefully, this post helps you build snappier UIs with React 19's new `useOptimistic` hook!
