Skip to content

Advanced Topics

This chapter bundles together power-user techniques for building sophisticated, production-ready forms with @enlolab/forms. We cover dynamic field behaviour, conditional step navigation, theming with Tailwind, and internationalisation (i18n).

Prerequisite – You should be comfortable with the basics: configuration, steps, validation and submission.


@enlolab/forms allows hidden, disabled, and readOnly props to be functions that receive the full form values object. This enables runtime logic without extra wiring.

fields: {
wantsNewsletter: {
type: "checkbox",
label: "Subscribe to newsletter",
schema: z.boolean()
},
emailFrequency: {
type: "select",
label: "Frequency",
options: [
{ label: "Daily", value: "daily" },
{ label: "Weekly", value: "weekly" }
],
hidden: (values) => !values.wantsNewsletter,
schema: z.string().optional()
}
}
fields: {
totalPrice: {
type: "text",
label: "Total (€)",
readOnly: true,
value: (values) => values.qty * values.unitPrice
}
}

Internally FieldRenderer evaluates the function on every render via watch() from react-hook-form.


Beyond per-field logic, you can route users through different steps with condition groups. Each StepCondition supports multiple conditions and combines them with AND / OR.

steps: {
start: {
id: "start",
fields: ["age"],
next: [
{
conditions: [
{ field: "age", operator: "lessThan", value: 18 }
],
operator: "AND",
target: "underage"
}
],
defaultNext: "adult"
},
underage: {...},
adult: {...}
}

Evaluation happens inside evaluateStepCondition located in src/utils/stepUtils.ts.


@enlolab/forms relies on Tailwind CSS and components built with Radix UI (similar to ShadCN UI patterns). You can therefore customise appearance via your Tailwind config.

  1. Override colours by extending the theme.colors section.
  2. Inject design tokens (e.g. brand radius) using CSS variables and reference them in ShadCN components.

Example tailwind.config.js snippet:

module.exports = {
theme: {
extend: {
colors: {
primary: {
DEFAULT: "#004aad",
foreground: "#ffffff",
},
},
borderRadius: {
lg: "0.75rem",
},
},
},
};

While field labels are free strings (or HTML), some components have implicit locales:

  • DateRenderers: uses locale es by default via date-fns. Pass locale prop (roadmap) or wrap with your own renderer.
  • Validation messages: leverage Zod’s API to translate messages.
  • Success / error messages: supply HTML with translated strings (e.g., successMessage: t('thanks')).

To fully localise a form, pair @enlolab/forms with a library like i18next or react-i18next and inject translations into field configs.

fields: {
name: {
label: t('name'),
placeholder: t('name_placeholder'),
schema: z.string().min(2, t('name_error'))
}
}

  1. Create a new React component (e.g., ColorPicker).
  2. Add a case in packages/enlolab-forms/src/factory/fieldRenderers.tsx mapping type: 'color' to your component.
  3. Extend TypeScript types in packages/enlolab-forms/src/types/fields.ts.
  4. Update the Fields Guide in the documentation.

The renderer receives disabled, readOnly and value/onChange props; integrate with them to maintain consistency.


  • Lazy load heavy inputs (e.g., map pickers) using React lazy + Suspense.
  • Avoid large field arrays in a single step; split across steps for faster validation.
  • Memoise icons or use Iconify string names instead of embedding SVG every render.