A component to handle submitting forms.

Form

Usage

  • Use Form as a fieldset container that wraps other Pluma form controls, handles submission, and controls the disabled state of every child control.
  • Use Form for settings pages (profile details, workspace configuration, API key management), modal forms where the submit button lives in the Modal footer outside the form body, and inline editors embedded in drawers, popovers, or the page.
  • Give every form a title describing its purpose, and a description when extra context is needed. Add a description only when it supplements the title.
  • Give every form a primary action that submits it. Add a secondary action, such as canceling, only when an alternative to submitting exists.
  • Add only the fields required for completion — shorter forms lead to higher completion rates.
  • Pair Form with FormLayout, which handles field grouping and gap customization. FormLayout is not required but is recommended for consistency across the design system.
  • Use FormLayoutGroup inside FormLayout to place related short fields side by side (e.g. first and last name, or city and state). Each field in the group receives equal width.
  • Stack unrelated fields vertically — never group fields that don't share a logical relationship.

Behaviors

  • Disabled state (isDisabled) — makes all nested form controls non-interactive and blocks submission events while still displaying existing input values. Use it to restrict access or edits until permission is granted, or to display read-only data.
  • Auto-loading state (autoLoading) — locks all fields while the promise returned from onSubmit is pending, then re-enables them once it resolves or rejects.

Content

  • Display validation errors inline using the form control's built-in error message prop.
  • Never show errors as a separate summary at the top or bottom of the form.
  • State clearly what is missing or what action the user can take to resolve the error.

Implementation Notes

  • Form renders a <fieldset> internally to cascade disabled to every child control — never disable each child individually to disable the form.
  • autoLoading defaults to true in React but false in Ember. Set it explicitly when cross-framework behavior must match.
  • onSubmit calls preventDefault() on the native submit event, so the form never performs a native browser submission. Return a promise from onSubmit to trigger auto-loading.
  • Form exposes no title, description, or action props — render those as siblings and children around the form controls, and give the submit button type="submit".
  • Form is polymorphic via Box — as defaults to form.
  • FormLayout and FormLayoutGroup each accept a gap prop (space token, default 200) for row and column spacing respectively.
  • React: pass content as children. Ember: pass content via {{yield}} (default block).
  • Ember invocation: <PlumaForm @isDisabled={{this.isDisabled}}>, with <PlumaFormLayout> and <PlumaFormLayoutGroup> for layout.

On this page