Component reference
FormSubmit
A customizable submit button connected to the pending state of Form.
Customize the pending indicator
Create the form state with useForm, then keep FormSubmit inside Form. This complete example awaits a local operation and replaces the default ring with a colored dots Spinner.
FormSubmit — a submit button with a custom loader
FormSubmit belongs inside Form and inherits Button's presentation props: variant, appearance, size, radius, fullWidth, icons, className, style and classNames. Form owns type=submit and loading through isSubmitting, so an asynchronous onSubmit drives the pending state automatically.
Provide loadingIndicator to replace the ring. Configure the Spinner's appearance and variant to change its shape and color, or supply your own decorative content. classNames.spinner styles its wrapper independently of the label. Omit the prop for the default ring, or pass null to hide the visual. This new prop awaits the next package release.
"use client";
import { useState } from "react";
import { Form, FormSubmit, Input, Spinner, useForm } from "@pimolu/react";
export function CustomSubmit() {
const [message, setMessage] = useState("");
const form = useForm({
defaultValues: { name: "Ada" },
onSubmit: async (values) => {
await new Promise((resolve) => setTimeout(resolve, 1200));
setMessage("Saved locally: " + values.name);
},
});
return (
<Form form={form} autoComplete="off" className="grid gap-4">
<Input label="Name" {...form.register("name")} />
<FormSubmit
appearance="solid"
loadingIndicator={<Spinner
appearance="dots"
size="sm"
variant="warning"
decorative
/>}
>
{form.isSubmitting ? "Saving…" : "Save locally"}
</FormSubmit>
<p role="status">{message}</p>
</Form>
);
}Integration notes
The loadingIndicator prop is available in 0.1.0-beta.4. See Usage and API reference below for the supported inherited Button props.
New to Pimolu? Follow the Quick start or the Next.js guide for the shared stylesheet and application provider.
Preview
Live previewOptional six-digit hex. Clear to restore the preset. Check contrast with your brand color.
Changes apply to this preview only.
Loading the interactive example?
Width adapts to the available space
"use client";
import { useState } from "react";
import { Form, FormSubmit, Input, useForm } from "@pimolu/react";
export function FormSubmitExample() {
const [message, setMessage] = useState("");
const form = useForm({
defaultValues: { name: "Ada" },
onSubmit: async (values) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
setMessage("Saved locally: " + values.name);
},
});
return (
<Form form={form} autoComplete="off" className="grid gap-4">
<Input label="Name" {...form.register("name")} />
<FormSubmit>
{form.isSubmitting ? "Saving…" : "Save profile"}
</FormSubmit>
<p role="status">{message}</p>
</Form>
);
}More examples
Different configurations and compositions using the real Pimolu API. Each preview runs independently and follows the site appearance.
Button appearances
Reuse Button's presentation props while Form owns submission.
Explicitly disable submission
Keep a submit action unavailable until the user accepts a condition.
Text-only pending feedback
Hide the decorative loader and keep a readable pending label.
Named-part customization
Target the submit button label and loader without changing the shared Button styles.
Usage
Place FormSubmit inside Form. It renders a Button with type=submit and reads form.isSubmitting automatically. Return or await your asynchronous work in useForm's onSubmit so the pending state lasts until that work completes.
Use Button's variant, appearance, size, radius, fullWidth, icons, disabled, className and style props. Do not pass type or loading: Form owns both. Prevent submission through validation rather than running business logic in onClick.
loadingIndicator accepts a decorative Spinner or custom React content. Configure Spinner's appearance, size and variant to change its shape and color. Omission keeps the ring; null/false hides the visual while retaining disabled and aria-busy. This prop ships in 0.1.0-beta.4.
Use classNames.root, label and spinner for named-part customization. Form-level classNames.submit styles the submit button; prefer the page's local classes when several forms need different treatments.
API reference
Generated from the public TypeScript declarations. Native HTML attributes are inherited where the component extends them; this reference includes Pimolu props and selected common native attributes.
PimoluFormSubmitProps
Type source| Name | Type | Default | Description |
|---|---|---|---|
classNames | PimoluClassNames<"form"> | undefined | — | Additive classes for stable named parts, including composed descendants. Parts: endIcon, error, label, root, spinner, startIcon, submit. Parts render only when the corresponding component or state is present. |
children | ReactNode | "Submit" | Public Pimolu prop; see the source declaration for details. |
onChange | ChangeEventHandler<HTMLButtonElement, Element> | undefined | — | Standard React/HTML attribute. |
defaultValue | string | number | readonly string[] | undefined | — | Standard React/HTML attribute. |
className | string | undefined | — | Standard React/HTML attribute. |
style | CSSProperties | undefined | — | Standard React/HTML attribute. |
aria-label | string | undefined | — | Defines a string value that labels the current element. |
onClick | MouseEventHandler<HTMLButtonElement> | undefined | — | Standard React/HTML attribute. |
variant | "primary" | "secondary" | "neutral" | "success" | "warning" | "danger" | undefined | — | Variant exposed by this component. |
appearance | "solid" | "soft" | "outline" | "ghost" | "link" | "dashed" | "glass" | "elevated" | "glow" | "text" | undefined | — | Visual treatment of the control or surface. |
size | "xs" | "sm" | "md" | "lg" | "xl" | "xxl" | undefined | — | Component size. |
radius | "sm" | "md" | "lg" | "none" | "full" | undefined | — | Corner-radius treatment. |
endIcon | ReactNode | — | Decorative content after the label or input. |
startIcon | ReactNode | — | Decorative content before the label or input. |
disabled | boolean | undefined | — | Standard React/HTML attribute. |
name | string | undefined | — | Standard React/HTML attribute. |
value | string | number | readonly string[] | undefined | — | Standard React/HTML attribute. |
loadingIndicator | ReactNode | — | Replaces the decorative loader while loading. undefined keeps the default ring; null/false hides it. |
fullWidth | boolean | undefined | — | Use the available width of the parent. |
iconOnly | boolean | undefined | — | Render an icon-only action. Provide an accessible name with aria-label. |
classNamesPimoluClassNames<"form"> | undefinedNo default extracted.
Additive classes for stable named parts, including composed descendants. Parts: endIcon, error, label, root, spinner, startIcon, submit. Parts render only when the corresponding component or state is present.
childrenReactNodeDefault: "Submit"
Public Pimolu prop; see the source declaration for details.
onChangeChangeEventHandler<HTMLButtonElement, Element> | undefinedNo default extracted.
Standard React/HTML attribute.
defaultValuestring | number | readonly string[] | undefinedNo default extracted.
Standard React/HTML attribute.
classNamestring | undefinedNo default extracted.
Standard React/HTML attribute.
styleCSSProperties | undefinedNo default extracted.
Standard React/HTML attribute.
aria-labelstring | undefinedNo default extracted.
Defines a string value that labels the current element.
onClickMouseEventHandler<HTMLButtonElement> | undefinedNo default extracted.
Standard React/HTML attribute.
variant"primary" | "secondary" | "neutral" | "success" | "warning" | "danger" | undefinedNo default extracted.
Variant exposed by this component.
appearance"solid" | "soft" | "outline" | "ghost" | "link" | "dashed" | "glass" | "elevated" | "glow" | "text" | undefinedNo default extracted.
Visual treatment of the control or surface.
size"xs" | "sm" | "md" | "lg" | "xl" | "xxl" | undefinedNo default extracted.
Component size.
radius"sm" | "md" | "lg" | "none" | "full" | undefinedNo default extracted.
Corner-radius treatment.
endIconReactNodeNo default extracted.
Decorative content after the label or input.
startIconReactNodeNo default extracted.
Decorative content before the label or input.
disabledboolean | undefinedNo default extracted.
Standard React/HTML attribute.
namestring | undefinedNo default extracted.
Standard React/HTML attribute.
valuestring | number | readonly string[] | undefinedNo default extracted.
Standard React/HTML attribute.
loadingIndicatorReactNodeNo default extracted.
Replaces the decorative loader while loading. undefined keeps the default ring; null/false hides it.
fullWidthboolean | undefinedNo default extracted.
Use the available width of the parent.
iconOnlyboolean | undefinedNo default extracted.
Render an icon-only action. Provide an accessible name with aria-label.
Accessibility
- Keep an explicit action label. During submission the button is disabled and exposes aria-busy. A decorative loader does not replace a readable pending or completion message.
- Use a polite status message for the result. Do not nest interactive controls inside loadingIndicator. Pimolu Spinner supports reduced motion; custom animations must honor that preference too.