form-refactorer

redpanda-data's avatarfrom redpanda-data

Refactor legacy forms to use modern Redpanda UI Registry Field components with react-hook-form and Zod validation. Use when user requests: (1) Form refactoring or modernization, (2) Converting Chakra UI or @redpanda-data/ui forms, (3) Updating forms to use Field components, (4) Migrating from legacy form patterns, (5) Implementing forms with react-hook-form and Zod validation.

⭐4.2kstars🔀409forks📁View on GitHub🕐Updated Jan 9, 2026

When & Why to Use This Skill

This Claude skill automates the modernization of legacy React forms by refactoring them into the latest Redpanda UI Registry Field components. It streamlines the transition from outdated libraries like Chakra UI or Yup to a high-performance, type-safe stack featuring react-hook-form and Zod validation. By enforcing modern coding standards and accessibility best practices, it helps developers significantly reduce technical debt and improve form maintainability.

Use Cases

  • Modernizing legacy Chakra UI or @redpanda-data/ui form structures to the standardized Redpanda UI Registry system.
  • Migrating form validation logic from Yup to Zod to leverage better TypeScript integration and schema-based validation.
  • Refactoring manual or legacy form state management to react-hook-form to minimize re-renders and boilerplate code.
  • Ensuring web accessibility (A11y) compliance by automatically implementing correct ARIA attributes, label associations, and semantic FieldSet structures.
  • Standardizing complex form layouts across a codebase using consistent FieldGroup and Field component hierarchies.
nameform-refactorer
descriptionRefactor legacy forms to use modern Redpanda UI Registry Field components with react-hook-form and Zod validation. Use when user requests: (1) Form refactoring or modernization, (2) Converting Chakra UI or @redpanda-data/ui forms, (3) Updating forms to use Field components, (4) Migrating from legacy form patterns, (5) Implementing forms with react-hook-form and Zod validation.

Form Refactorer

Refactor legacy forms to modern Redpanda UI Registry Field components with react-hook-form and Zod validation.

Core Requirements

MUST USE (Modern Pattern)

import {
  FieldSet,
  FieldLegend,
  FieldGroup,
  Field,
  FieldLabel,
  FieldContent,
  FieldDescription,
  FieldError,
  FieldSeparator
} from "components/redpanda-ui/components/field";
  • react-hook-form for form state management
  • Zod for validation schemas via zodResolver
  • Field component family for all form layouts

FORBIDDEN (Legacy Patterns)

See no-legacy for prohibited patterns.

// ❌ Also never import legacy form components
import { Form } from "components/redpanda-ui/components/form";

Refactoring Workflow

Step 1: Identify Legacy Patterns

Scan the form file for these indicators:

Legacy imports to remove:

  • @chakra-ui/react form components (FormControl, FormLabel, FormErrorMessage, FormHelperText)
  • @redpanda-data/ui Form component
  • components/redpanda-ui/components/form (legacy)
  • Yup validation imports

Legacy patterns to replace:

  • <FormControl isInvalid={...}> → <Field data-invalid={...}>
  • <FormLabel> → <FieldLabel htmlFor="...">
  • <FormErrorMessage> → <FieldError>
  • <FormHelperText> → <FieldDescription>
  • Yup schemas → Zod schemas

Step 2: Set Up Modern Dependencies

Add required imports:

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
  FieldSet,
  FieldLegend,
  FieldGroup,
  Field,
  FieldLabel,
  FieldError,
  FieldDescription
} from "components/redpanda-ui/components/field";

Create Zod schema replacing Yup:

// Replace Yup schema
const schema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email address"),
  age: z.number().min(18, "Must be 18 or older"),
});

// Use with react-hook-form
const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: zodResolver(schema),
});

Step 3: Restructure Form Layout

Replace legacy structure with Field components:

Pattern: Simple vertical field

<Field data-invalid={!!errors.fieldName}>
  <FieldLabel htmlFor="fieldName">Field Label</FieldLabel>
  <Input
    id="fieldName"
    {...register("fieldName")}
    aria-invalid={!!errors.fieldName}
  />
  <FieldDescription>Optional helper text</FieldDescription>
  {errors.fieldName && <FieldError>{errors.fieldName.message}</FieldError>}
</Field>

Pattern: Horizontal field (switch/checkbox)

<Field orientation="horizontal">
  <Switch id="toggle" {...register("toggle")} />
  <FieldContent>
    <FieldLabel htmlFor="toggle">Enable Feature</FieldLabel>
    <FieldDescription>This enables the feature</FieldDescription>
  </FieldContent>
</Field>

Pattern: Multiple fields in group

<FieldSet>
  <FieldLegend>Section Title</FieldLegend>
  <FieldDescription>Section description (optional)</FieldDescription>
  <FieldGroup>
    <Field>...</Field>
    <Field>...</Field>
    <FieldSeparator />  {/* Optional visual divider */}
    <Field>...</Field>
  </FieldGroup>
</FieldSet>

Step 4: Handle Validation Errors

Apply error states to both Field and Input:

<Field data-invalid={!!errors.email}>
  <FieldLabel htmlFor="email">Email</FieldLabel>
  <Input
    id="email"
    {...register("email")}
    aria-invalid={!!errors.email}  // For assistive technologies
  />
  {errors.email && <FieldError>{errors.email.message}</FieldError>}
</Field>

Step 5: Ensure Accessibility

Required accessibility attributes:

  1. Label association: Use htmlFor on FieldLabel matching Input id
  2. Error state: Add data-invalid to Field when error exists
  3. ARIA invalid: Add aria-invalid to Input when error exists
  4. Semantic grouping: Use FieldSet + FieldLegend for related fields
// ✅ Correct accessibility
<Field data-invalid={!!errors.name}>
  <FieldLabel htmlFor="name">Name</FieldLabel>
  <Input id="name" {...register("name")} aria-invalid={!!errors.name} />
  {errors.name && <FieldError>{errors.name.message}</FieldError>}
</Field>

Quick Reference

Component Hierarchy

FieldSet              - Semantic fieldset container for related fields
├─ FieldLegend        - Title for the fieldset
├─ FieldDescription   - Description for entire fieldset
└─ FieldGroup         - Layout wrapper that stacks fields
   ├─ Field           - Individual field wrapper
   │  ├─ FieldLabel       - Label for input (use htmlFor)
   │  ├─ Input/Select/... - Form control
   │  ├─ FieldDescription - Helper text
   │  └─ FieldError       - Error message
   ├─ FieldSeparator  - Visual divider between fields
   └─ Field           - Another field

Common Zod Patterns

// String validations
z.string().min(1, "Required")
z.string().email("Invalid email")
z.string().url("Invalid URL")
z.string().regex(/^[A-Z]/, "Must start with capital")

// Number validations
z.number().min(0, "Must be positive")
z.number().max(100, "Max 100")
z.number().int("Must be integer")

// Optional fields
z.string().optional()
z.string().nullable()

// Arrays
z.array(z.string()).min(1, "At least one required")

// Objects
z.object({
  nested: z.string(),
})

// Conditional validation
z.object({
  type: z.enum(["a", "b"]),
  value: z.string(),
}).refine((data) => {
  if (data.type === "a") return data.value.length > 5;
  return true;
}, "Value must be longer than 5 for type A")

Advanced Scenarios

For complex patterns (dynamic fields, field arrays, multi-section forms), see:

Checklist

Before completing refactoring, verify:

  • All legacy form imports removed (Chakra, @redpanda-data/ui, legacy Form)
  • Field components used for all fields
  • FieldLabel has htmlFor matching Input id
  • Errors show data-invalid on Field and aria-invalid on Input
  • Yup replaced with Zod + zodResolver
  • FieldSet + FieldLegend used for semantic grouping
  • FieldError conditionally rendered: {errors.field && <FieldError>...
  • No inline styling or className for layout (use Field orientation/FieldGroup)
  • Form submission handled via react-hook-form handleSubmit

Documentation

Full Field component documentation: https://redpanda-ui-registry.netlify.app/docs/field