Skip to content

SubmitButton

A form’s submit button, and the busy one that replaces it while the request is on its way.

import { SubmitButton } from '@zitdevs/ui';

This form posts natively, into a hidden frame so this page stays put. The page runs enhanceSubmitState(document) from @zitdevs/ui/enhance; press Send to see the sending state.

In each size, before anything is sent:

<form method="post" action="/contact#zd-result">
{/* the questions */}
<SubmitButton label="Send request" sendingLabel="Sending request…" />
</form>

A submit the page cancels — client-side validation, a React onSubmit that calls preventDefault, a function action — never navigates, so the button leaves it alone. Pass pending to show the sending state for the request the page makes, from your own state or React’s useFormStatus:

'use client';
const [pending, setPending] = useState(false);
<form
onSubmit={async (event) => {
event.preventDefault();
setPending(true);
await send();
setPending(false);
}}
>
<SubmitButton label="Send" sendingLabel="Sending…" pending={pending} />
</form>;
Prop Type Default Notes
label string required The send button’s words.
sendingLabel string required The busy button’s words, while the request is on its way.
size 'sm' | 'md' | 'lg' 'lg' As Button’s.
pending boolean — For a form the page sends itself: the sending state shows while true.
ref Ref<HTMLButtonElement> — The send button.
className string — Placement, on both buttons: one stands in for the other.
anything else <button> attributes — On the send button: name, value, form…

Exported types: SubmitButtonProps. From @zitdevs/ui/enhance: enhanceSubmitState, SubmitStateEnhancement (reset(), setPending()).

  • The send button is a native type="submit"; the busy one is a type="button" with aria-busy="true" and a spinner that holds still for reduced motion.
  • On a submit, focus moves to the busy button, the one the reader is now watching, and comes back to the send button when the state clears.
  • A long label wraps inside the button, since the copy decides how long it is.
  • Another submit button of the same form, such as an “Edit” with its own formaction, sends without this spinner; a second submit is blocked either way.

The form posts natively; the button is a plain submit. The busy button is in the page already, hidden, so the enhancer draws nothing:

  • React: nothing to do. The send button is a small client part that runs the enhancer once it has hydrated.
  • No React: load @zitdevs/ui/enhance and call enhanceSubmitState(document). The back/forward cache restores a page as it was left, still spinning, so it resets on pageshow.

The page needs Tailwind’s preflight, or a reset with its [hidden] rule: the busy button is a flex box switched with hidden.

Button’s primary variant, and its busy state.