Skip to content

Buttons Guide

Buttons are more than decoration—they drive the user flow and communicate state. @enlolab/forms ships with three first-class buttonssubmit, next, and back—which you can fully customise via the buttons property in FormConfig.

Key takeaway – Every button is just a configuration object; no need to re-implement React components unless you want to.

If you omit buttons, @enlolab/forms injects sensible defaults:

ButtonDefault LabelVariantSize
submitSubmitdefaultmd
nextSiguiente →outlinemd
back← Atrásghostmd

They automatically appear at the end of each step:

  • Back is hidden on the first step (when stepHistory.length === 0).
  • Next is replaced by Submit on the last step (when isLast === true).
PropertyTypeRequiredDescription
type"button" | "submit" | "next" | "back"✔︎Determines behaviour & placement.
labelstring✖︎Text shown inside the button.
variantShadCN variant✖︎default, destructive, outline, secondary, ghost, link.
size"xs" "sm" "md" "lg" "xl"✖︎Tailwind-based size classes.
effectSee Effects below✖︎Adds interactive motion using CSS.
iconReactNode | string✖︎Pass a Lucide element or an Iconify string.
iconPosition"left" | "right"✖︎Defaults to left.
disabledboolean | (values) => boolean✖︎Control interactively.
action() => void✖︎Custom onClick handler (ignored for submit).
loadingboolean✖︎Overrides the spinner state.
colSpanGrid span✖︎Make the button wider/narrower.

Variants map directly to ShadCN UI classes—you inherit its full design system. Combine with size for compact or prominent CTAs:

buttons: {
submit: {
variant: "destructive",
size: "lg"
}
}

Built-in effects are pure CSS and cost zero JS:

KeyDescription
expandIconIcon glides slightly when hovered.
ringHoverShows a subtle ring outline on hover.
shineAnimated diagonal shine (continuous).
shineHoverShine only on hover.
gooeyRightGooey blob animation moving right.
gooeyLeftGooey blob moving left.
underlineClassic underline expansion.
hoverUnderlineUnderline appears only on hover.

Example:

buttons: {
next: {
effect: "ringHover";
}
}

Icons improve scannability. You can pass:

import { ArrowRight } from "lucide-react";
buttons: {
next: {
icon: <ArrowRight />, // React element
iconPosition: "right"
},
submit: {
icon: "mdi:send", // Iconify string
effect: "expandIcon"
}
}

Internally @enlolab/forms delegates to the helper getIcon() which supports both React nodes and Iconify names.

Need to disable Submit until terms are accepted? Use a function:

buttons: {
submit: {
disabled: (values) => !values.agreeToTerms;
}
}

To show a spinner during async work set loading: true. @enlolab/forms automatically adds a Loading… label but you can override via label.

While submit triggers the internal handleSubmit logic, next and back can invoke custom callbacks:

buttons: {
next: {
action: () => {
console.log("analytics: user clicked next");
};
}
}

Note: The internal navigation is still executed after your action.

You can also create additional arbitrary buttons by adding a field of type button inside fields and placing it in any step.

  1. Use semantic <button> elements – already handled by @enlolab/forms.
  2. Provide clear, descriptive labels (label prop). Avoid jargon.
  3. Ensure sufficient color contrast when changing variants.
  4. Display a loading indicator for operations > 500 ms.
  5. Disable buttons only when absolutely necessary; explain why.