← Все статьи

Типизированный Tailwind съел React Hook Form: история DashForge

Как автор превратил Tailwind-классы в typed props и дошёл до «осознанных» компонентов: формы, visibleWhen, RBAC — без Controller на каждом поле.

Содержание

Коротко

На Dev.to автор DashForge рассказывает, как проблема «форм в React» оказалась не CSS, а проводкой: Controller, watch, useEffect на каждое поле. Ответ — typed props вместо className-супа, мост к React Hook Form и компоненты, которые сами знают форму, видимость и права доступа.

Что произошло

Стартовая боль знакома: Tailwind решает cascade и мёртвые стили, но визуальная часть живёт в непрозрачной строке без автодополнения и рефакторинга. Автор спросил: что если variant, color, size — обычные типизированные props, а за кадром — utilities через tailwind-variants и design tokens?

Следующий слой — формы. Паттерн RHF с Controller и render prop на 50-м поле утомляет. TextField с name="email" сам читает value, ошибки и blur/submit-логику через context-мост — без ручной проводки.

Дальше — оркестрация бизнес-UI:

  • visibleWhen — поле описывает условие показа (customerType === business), без watch + useEffect на каждое правило.
  • access на кнопке — invoice:update внутри компонента, а не permissions.includes в каждом экране.

Архитектурный поворот: две UI-библиотеки (MUI и Tailwind + Radix) с одинаковыми props оркестрации, но общим headless-движком (мост форм, видимость полей, RBAC). Стили сменяемы; «интеллект» приложения — в ядре.

Почему это важно

В enterprise-формах основная стоимость — не пиксели, а glue-код: связать поле с формой, валидацией, правами, условной видимостью. Typed visual API снижает класс ошибок в стилях; application-aware components — повторяющийся boilerplate на сотнях экранов.

Это не «ещё один UI-kit»: DashForge в alpha, но идея — вынести повторяющуюся оркестрацию в декларативные props, оставив Radix/React Aria для a11y.

На практике

  1. Visual API — props-first поверх Tailwind; один escape hatch sx с tailwind-merge.
  2. Поля<TextField name="…" label="…" /> вместо Controller + error plumbing.
  3. УсловияvisibleWhen={{ field, equals }} вместо watch/effect.
  4. Праваaccess="resource:action" на интерактивных узлах.
  5. Оценка зрелости — alpha; смотреть @dashforge/tw и repo перед adoption в продакшен.
Было Стало
className-строка typed variant/color/size
Controller × N context bridge
watch + effect × правила visibleWhen
permissions && в JSX access на Button

Итог

История — про смещение фокуса с «как стилизовать» на «как компонент участвует в приложении». Если устали от copy-paste проводки форм — имеет смысл посмотреть DashForge как эксперимент, не как готовую замену всему стеку.