Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Form Loading State Block for Ark UI

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.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Show both loading states of one form with this form loading state for React with Ark UI. 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.

How do I connect it to react-hook-form or TanStack Form?

With react-hook-form, pass your fetch result to reset(values) when it arrives and use formState.isSubmitting for the saving phase; show the skeleton while the query is pending, before reset runs.