Skip to content

Consuming a feature form

Forms are React Aria Components plus one Zod schema, submitted through the feature’s mutation hook. Your job is to render, wire and brand — never to re-implement validation.

Read the package’s own docs first: which form view it exports, its props, and the Zod schema it exports, which you reuse rather than restate.

"use client";
import { AddressForm } from "@evolve-storefront/feature-account/address-form";
import { useUpdateAddress } from "@evolve-storefront/feature-account";
export function AddressPanel({
defaultValues,
}: {
defaultValues?: AddressInput;
}) {
const { mutate, error } = useUpdateAddress();
return (
<AddressForm
defaultValues={defaultValues}
serverErrors={error?.fieldErrors}
onValid={(data) => mutate(data)}
/>
);
}

Keep the page thin. Submit goes through the mutation hook, not a Server Action — storefront forms are write-then-stay-and-reconcile, which is what the entity cache is for.

Do not fight or reproduce it. Clean on load, with no errors while the user is entering. On the first submit validation becomes visible and submitting is blocked until it is fixed; after that, feedback is live as fields are corrected. The submit button is never disabled — clicking always says why the form is not valid rather than presenting a dead button.

This is deliberately not React Aria’s default, which reveals errors on blur. It works because all validation is driven through validationErrors rather than native constraints — so do not mix in isRequired or pattern for display. They fire on blur and fight the timing.

Never trust the client. Run the same exported schema on the backend, return error.flatten().fieldErrors, and feed them into the form’s serverErrors. Both paths render through the same validationErrors map, so client and server errors look and clear identically.

Localize error messages server-side — React Aria does not localize custom messages on the client.

Add a field by composing it into the slot the package exposes and extending the exported schema through its extension seam, so client and server cannot drift.

React Hook Form and TanStack Form stay available as an escalation for genuinely complex client forms — dynamic field arrays, multi-step wizard state. Rare in a storefront, never the default.