Cómo Crear Formularios Accesibles Usando ShadCN UI

Crear formularios que sean tanto funcionales como accesibles puede ser desafiante. Entre gestionar el estado, manejar la validación y asegurar los atributos ARIA apropiados, los desarrolladores a menudo pasan incontables horas en lo que deberían ser tareas sencillas. ShadCN UI, combinado con React Hook Form y Zod, ofrece una solución poderosa que simplifica la creación de formularios mientras mantiene los estándares de accesibilidad.
Este artículo demuestra cómo construir formularios accesibles con ShadCN UI, integrar ShadCN UI React Hook Form para la gestión de estado, e implementar patrones de validación ShadCN UI Zod que permanecerán relevantes durante años.
Puntos Clave
- ShadCN UI maneja automáticamente los atributos ARIA y patrones de accesibilidad a través de su sistema de formularios componible
- La integración con React Hook Form proporciona gestión eficiente del estado con re-renderizados mínimos
- La validación de esquemas Zod asegura validación de formularios type-safe con mensajes de error claros
- Los componentes integrados manejan asociaciones de etiquetas, anuncios de errores y navegación por teclado
Entendiendo la Arquitectura de Formularios de ShadCN UI
ShadCN UI proporciona un sistema de formularios componible construido sobre primitivos de React Hook Form y Radix UI. La arquitectura sigue un patrón consistente:
<Form>
<FormField
control={form.control}
name="fieldName"
render={({ field }) => (
<FormItem>
<FormLabel>Field Label</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormDescription>Helper text</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</Form>
Esta estructura maneja automáticamente:
- Generación de ID únicos usando
React.useId()
- Atributos
aria-describedby
yaria-invalid
apropiados - Asociaciones de mensajes de error
- Relaciones etiqueta-input
Configurando Formularios Accesibles con ShadCN UI
Primero, instala los componentes necesarios:
npx shadcn@latest add form input textarea checkbox label
Este comando instala los componentes de ShadCN UI junto con las dependencias de React Hook Form y Zod.
Creando un Esquema de Formulario Básico
Define la estructura de tu formulario usando Zod para validación type-safe:
import { z } from "zod"
const formSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Invalid email address"),
message: z.string().min(10, "Message must be at least 10 characters"),
terms: z.boolean().refine((val) => val === true, {
message: "You must accept the terms"
})
})
type FormData = z.infer<typeof formSchema>
Implementando el Componente de Formulario
Aquí te mostramos cómo construir un formulario de contacto accesible usando la integración de ShadCN UI React Hook Form:
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Checkbox } from "@/components/ui/checkbox"
import { Button } from "@/components/ui/button"
export function ContactForm() {
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
email: "",
message: "",
terms: false
}
})
function onSubmit(data: FormData) {
console.log(data)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="John Doe" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="john@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="message"
render={({ field }) => (
<FormItem>
<FormLabel>Message</FormLabel>
<FormControl>
<Textarea
placeholder="Your message here..."
className="resize-none"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="terms"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
Accept terms and conditions
</FormLabel>
</div>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
Discover how at OpenReplay.com.
Características de Accesibilidad Integradas
Los componentes de formulario de ShadCN UI implementan automáticamente patrones de accesibilidad cruciales:
- Etiquetado Apropiado: El componente
FormLabel
usa el atributohtmlFor
para asociarse con los controles de formulario - Anuncios de Error: Los componentes
FormMessage
se vinculan mediantearia-describedby
- Estados Inválidos: Los campos reciben automáticamente
aria-invalid="true"
cuando falla la validación - Navegación por Teclado: Todos los componentes soportan interacciones estándar de teclado
Patrones de Validación Avanzados
Implementa escenarios complejos de validación ShadCN UI Zod:
const advancedSchema = z.object({
password: z.string()
.min(8, "Password must be at least 8 characters")
.regex(/[A-Z]/, "Password must contain at least one uppercase letter")
.regex(/[a-z]/, "Password must contain at least one lowercase letter")
.regex(/[0-9]/, "Password must contain at least one number"),
confirmPassword: z.string()
}).refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"]
})
Mejores Prácticas para Formularios Accesibles
- Siempre Usa Etiquetas: Cada input necesita una etiqueta visible para usuarios de lectores de pantalla
- Proporciona Mensajes de Error Claros: Sé específico sobre qué salió mal y cómo solucionarlo
- Agrupa Campos Relacionados: Usa
fieldset
ylegend
para agrupaciones lógicas - Prueba con Teclado: Asegúrate de que todas las interacciones funcionen sin ratón
- Valida al Enviar: Evita validación inline agresiva que interrumpa a los usuarios
Conclusión
ShadCN UI transforma el desarrollo de formularios combinando el poder de la gestión de estado de React Hook Form con la validación type-safe de Zod, todo mientras mantiene la accesibilidad como principio fundamental. La arquitectura de componentes asegura que cada formulario que construyas cumpla con los estándares WCAG sin requerir gestión manual de atributos ARIA.
Siguiendo estos patrones, creas formularios que funcionan para todos, independientemente de sus habilidades o tecnologías asistivas. La belleza de este enfoque radica en su simplicidad: los formularios accesibles se convierten en el estándar, no en una idea tardía.
Preguntas Frecuentes
Aunque técnicamente es posible, los componentes de formulario de ShadCN UI están diseñados para trabajar con React Hook Form. Usarlos sin él significa perder validación automática, gestión de estado y características de accesibilidad que hacen valiosa la librería.
ShadCN UI genera automáticamente IDs únicos, asocia etiquetas con inputs a través de atributos htmlFor, vincula mensajes de error mediante aria-describedby, y establece aria-invalid en errores de validación. Todos los componentes soportan navegación por teclado por defecto.
La validación de Zod se ejecuta solo cuando es necesario, típicamente en eventos blur o submit. El impacto en el rendimiento es mínimo ya que React Hook Form previene re-renderizados innecesarios. Para formularios con cientos de campos, considera usar validación a nivel de campo en lugar de validación a nivel de esquema.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.