shadcn.io is not affiliated with official shadcn/ui
Form Loading State Block for React Aria
An edit-employee-record form that opens as a skeleton in the same cells as its fields, then saves for a second and a half with frozen fields, a progress line and a Cancel that stops it.
Installation
Overview
Show both loading states of one form with this form loading state for React with React Aria. A compact header with one Reload record button sits over a hairline frame with a plus mark on each corner. Fields live in hairline cells: name, job title, department, work location, start date, manager and notes. The form opens as a skeleton for a moment, as if fetching the record: the real labels stay, grey boxes stand where the controls will be, and the region is marked busy. Then it is editable. Save record runs for a second and a half with every field frozen in a disabled fieldset, the button saying "Saving" with a spinner, a thin progress line filling above the foot, and a Cancel that stops it and keeps what you typed. Reload record plays the skeleton again. The Shine Button is the only colored control. It is built from Skeleton, Input, Native Select, Textarea, Shine Button and Guides. For any record editor that fetches before it shows and saves over the network.
FAQ
How does it avoid layout shift between the skeleton and the form?
Each cell holds its label, then a grid cell with the skeleton and the real control stacked in it. Only one is visible; the other is invisible and inert but still takes its space. The skeleton box has the same height as the control it stands for, so the cell never changes size, and the controls cannot be reached while the skeleton is showing.
What happens while it saves?
The fields are in a fieldset disabled, so nothing can be edited or tabbed to, while the Cancel button is outside the fieldset and stays live; focus is moved to it when the save starts. The Shine Button shows its loading state with the label "Saving", and the progress line is a width transition that finishes in the save time (under reduced motion it shows full at once).
What does Cancel do?
It clears the pending timer, so the save never completes, and returns the fields to editing with everything you typed. The status line says "Save cancelled. Nothing changed." With a real request, call abort() on an AbortController there and send nothing more.
How does it work with a screen reader?
The form carries aria-busy while the skeleton shows, the skeleton boxes are hidden from assistive technology, and the foot status is the one polite region: "Loading the record…", "Saving the record…", then the result. Save record and Reload record use aria-disabled while they cannot run, so they keep focus and answer when pressed.
What stays local, and how do I connect it for real?
Both waits are timers and the record is a constant in the file. To make it real, replace the 1.2 second timer with your fetch and set the form's values from its result, and replace the save timer with your request, keeping phase as the single source of truth for what is shown.