Repository navigation
✨ New rule - Do you use optimistic updates so the UI feels instant? - #13633
Conversation
🔍 Preview PR ChangesView updated pages in edit mode:
|
|
Hi @GordonBeeming, Rule: ../../public/uploads/rules/use-optimistic-updates/rule.mdxIssues Detected:
Please fix the Frontmatter in your Markdown files. |
|
👋 Hi @GordonBeeming, This PR is currently in Draft mode. To help it get reviewed and merged:
Once you've completed these steps, your PR will be ready for approval! This is an automated reminder to help reduce draft PRs without descriptions or reviewers. |
There was a problem hiding this comment.
🟡 Changes recommended
Whole-list rollback and premature refetching can overwrite unrelated saved or pending renames, while the conflict message incorrectly presents stale data as current.
3 open issues
What changed in this PR
Adds an SSW rule explaining how to make UI updates feel instant while protecting saved data with server-side concurrency checks.
Changes:
- Explains when to use optimistic updates, with TanStack Query examples for rollback and re-sync.
- Demonstrates EF Core rowversion checks,
If-Matchheaders, and client-side conflict handling. - Adds the rule to the React and Web API category indexes.
| File | Description |
|---|---|
public/uploads/rules/use-optimistic-updates/rule.mdx |
New guidance and examples for optimistic updates and concurrency control. |
categories/software-engineering/rules-to-better-web-api.mdx |
Adds the rule to the Web API category. |
categories/software-engineering/rules-to-better-react.mdx |
Adds the rule to the React category. |
Review effort: Balanced
| onError: (error, _variables, context) => { | ||
| queryClient.setQueryData(heroesKey, context?.previousHeroes); |
|
|
||
| toast.error( | ||
| error instanceof ConcurrencyConflictError | ||
| ? "Someone else changed this hero. Their latest changes are now showing." |
| ); | ||
| }, | ||
|
|
||
| onSettled: () => queryClient.invalidateQueries({ queryKey: heroesKey }), |

https://www.ssw.com.au/rules/use-optimistic-updates
Why: Showing a spinner and waiting for the API on every small change makes an app feel slow, even when almost every save succeeds. Updating the UI straight away and rolling back on failure makes it feel instant, and server-side concurrency control makes sure an optimistic save based on stale data is rejected instead of silently overwriting someone else's change.
Key points:
onMutate, roll back inonError, replace with the saved item inonSuccess, and re-sync inonSettledIf-Match, and return412 Precondition Failed(or409 Conflictfor a body version) on a mismatchTriggered by: A real project where the team switched from spinners to optimistic updates and needed the API to catch conflicting writes from concurrent users.