shadcn.io is not affiliated with official shadcn/ui
Form Error State Block for React Aria
A create-a-site form whose first send fails by script with a dismissible alert, Retry and Edit, then a repository-not-found error on its own field, so you can see how a failed send recovers.
Installation
Overview
Show a failed send and the way back with this form error state for React with React Aria. A compact header over a hairline frame with a plus mark on each corner introduces a create-a-site form already filled in: a site name, a region, a repository with a typo and optional build notes. The first Create site fails by script. A dismissible alert appears in a band that was held for it, reading "We could not reach the build service. Your answers are kept." with Retry and Edit answers, and focus lands on it. Retry reaches the service, which rejects one field: "Repository not found" appears under the repository, tied to it with aria-describedby, and focus moves there. Correct the name and Create site succeeds. The Shine Button is the only colored control. It is built from Alert, Input, Native Select, Textarea, Shine Button and Guides. For any form that talks to a server that can fail, in a way the visitor can recover from.
FAQ
How is the failure scripted?
A counter in a ref counts the requests. The first one always ends as a network failure; every later one reaches the pretend service, which knows two repositories and rejects any other as "Repository not found". Start over resets the counter and the answers, so the whole sequence can be replayed.
Why are there two kinds of error?
They need different treatment. A failure that is not about any one answer (the service was unreachable) goes in a banner with Retry, since nothing the visitor typed was wrong. An error about one answer goes on that field, where the visitor can fix it. The banner says the answers are kept so Retry never feels like starting over.
Why do the fields not move when the alert appears?
The intro note and the alert share one grid cell. The alert is always rendered, invisible and inert until it is needed, and it is taller than the note, so the cell already has its height. Dismissing it only hides it.
How does it work with a keyboard and a screen reader?
The alert has role="alert" and takes focus when it appears, so it is read at once and Retry is one Tab away. The field error is tied to its input with aria-describedby, the input is marked aria-invalid, and focus goes to it. The foot status is the one polite region and says which step happened.
What stays local, and how do I connect it for real?
The requests are timers and nothing is sent. To make it real, replace the timer with your API call; on a network error show the banner, and on a validation response map each field error into the problems state by field name so it lands under its input.