Me crucé con react-hook-form al integrar el paquete de formularios de shadcn y me encontré con varios componentes utilitarios como Form, FormField, FormControl, FormLabel y FormMessage. Si te has sentido abrumado al combinar librerías headless como Radix con react-hook-form, aquí tienes una guía clara y práctica en español para entender cuándo usar register, Controller y useController y cómo hacer que todo conviva de forma limpia y escalable en proyectos de aplicaciones a medida.
Componente controlado vs no controlado
La diferencia clave es dónde se gestiona el estado. Si el DOM lleva el control del valor y accedes a él mediante ref, estás ante un componente no controlado. Si React guarda el valor en su estado y lo actualizas en respuesta a eventos, es un componente controlado. Esta distinción te ayudará a elegir la estrategia correcta en cada campo del formulario.
register
Con register el estado se mantiene en el DOM, por lo que el campo actúa como no controlado. Es la opción ideal para elementos nativos como input, select, textarea y checkbox. Ventajas habituales: menos re renders, mayor rendimiento, validación declarativa y un API muy simple. Usa register siempre que el valor del campo pueda mapearse a un value nativo de HTML sin transformaciones complejas.
Controller
Controller convierte el campo en controlado por React. Es la herramienta adecuada cuando el valor no encaja con un input nativo o requiere transformación. Ejemplos típicos: un Slider de rango con mínimo y máximo, un Rating, un Color Picker o fechas avanzadas. Imagina un selector de precios con dos manejadores; necesitas mapear los eventos de cada extremo a un valor compuesto, por ejemplo un array con [mínimo, máximo]. Controller te da acceso a value y onChange para moldear ese flujo con precisión.
useController
useController es la versión en hook de Controller. Te devuelve field y fieldState para integrarte en componentes personalizados sin inflar el JSX con componentes wrapper. Es especialmente útil al encapsular lógica de formularios en componentes reutilizables y minimizar el movimiento ocular al leer el JSX. Aun así, en pantallas con muchos campos, múltiples llamadas a useController pueden resultar verbosas; en esos casos, un Controller por campo puede mejorar la legibilidad.
register vs Controller reglas prácticas
Elige register cuando uses elementos nativos y no necesites transformar el valor. Si el componente es sofisticado o su valor es compuesto o no estándar, opta por Controller. Cuando estés creando un componente propio reusable y quieras mantener el JSX limpio, considera useController. Si varios componentes del mismo formulario comparten transformaciones complejas del valor, Controller suele simplificar el mantenimiento.
Integración con shadcn y librerías headless
Los helpers de shadcn como Form, FormField, FormControl, FormLabel y FormMessage son adaptadores que centralizan etiquetas, controles y mensajes de error, y que pueden trabajar tanto con register como con Controller. La clave es decidir en cada FormField si conectas directamente register o si inyectas control y las funciones de transformación necesarias. En componentes headless de Radix, Controller o useController suelen ser la mejor vía para sincronizar su estado interno con react-hook-form.
Cuándo preferir estado global en lugar del formulario
Si el valor debe ser conocido por toda la aplicación, es mejor sacarlo del formulario y gestionarlo en un contexto global. Un ejemplo clásico es el tema claro u oscuro: ese estado afecta a toda la UI y debe propagarse por contexto, no como un campo del formulario. Usa el formulario para datos del dominio y contexto para estados transversales como tema, autenticación o preferencias.
Rendimiento y buenas prácticas
register suele implicar menos re renders y es la opción más eficiente. Controller re renderiza el componente controlado y conviene memorizar subcomponentes cuando sea necesario. Define defaultValues coherentes con la estructura esperada del campo, evita alternar sin necesidad entre controlado y no controlado, y usa shouldUnregister cuando quieras limpiar valores al desmontar campos condicionales. Para campos compuestos, diseña una única fuente de verdad y centraliza la transformación en onChange.
Errores frecuentes y cómo evitarlos
Desajustes entre defaultValues y el tipo real de value, olvidar pasar control al usar Controller o useController, depender de estados de UI globales dentro del formulario cuando deberían ir a contexto, y no normalizar valores de componentes complejos como Sliders o Ratings. Aclara el contrato del valor desde el inicio y valida siempre el shape final que enviarás al backend.
Conclusión rápida
Usa register para inputs nativos y simples. Recurre a Controller cuando necesites control completo, transformación de valores o integración con componentes headless. Elige useController para encapsular lógica en componentes reutilizables sin sobredimensionar el JSX. Combina estas piezas con los helpers de shadcn para obtener formularios consistentes, accesibles y fáciles de mantener en productos de software a medida y aplicaciones a medida.
Sobre Q2BSTUDIO
En Q2BSTUDIO desarrollamos software a medida y aplicaciones a medida con foco en calidad, escalabilidad y experiencia de usuario. Integramos formularios complejos con react hook form, diseñamos APIs robustas y automatizamos procesos de negocio con agentes IA. Nuestro equipo también ofrece inteligencia artificial e ia para empresas, ciberseguridad y pentesting, servicios cloud aws y azure, además de servicios inteligencia de negocio y analítica avanzada con power bi. Si buscas un partner tecnológico que entienda tu producto y te ayude a acelerar su roadmap, descubre nuestro enfoque en el desarrollo de software y aplicaciones a medida.