chore: react-hook-form best practices (#44221)

## Problem

The design system documentation and examples promote an invalid usage of
react-hook-form state.

## Solution

- Fix the documentation and examples
- Update Claude skills for future components

## Out of scope

- Fix current code across the repository
This commit is contained in:
Gildas Garcia authored and GitHub committed 2026-03-26 16:24:08 +01:00
1 parent fba5a8a001
commit 5c98bb6c0f
3 files changed
+9 -5

No files matched your search

+2 -3
View File
@@ -1,5 +1,5 @@
---
description: "Studio: form patterns (page layouts + side panels) and react-hook-form conventions"
description: 'Studio: form patterns (page layouts + side panels) and react-hook-form conventions'
globs:
- apps/studio/**/*.{ts,tsx}
alwaysApply: false
@@ -29,7 +29,6 @@ Use the Design System UI pattern docs as the source of truth:
## Actions and state
- Handle dirty state (`form.formState.isDirty`) to show Cancel and to disable Save.
- Handle dirty state by destructuring `isDirty` from `formState` (`const { isDirty } = form.formState`) then use it to show Cancel and to disable Save.
- Show loading on submit buttons via `loading`.
- When submit button is outside the `<form>`, set a stable `formId` and use the button’s `form` prop.
@@ -49,7 +49,7 @@ Use the shared [Key/Value Field Array](../fragments/key-value-field-array) fragm
4. **Use Cards for grouping**: Wrap form sections in `Card` components with `CardContent` and `CardFooter` for actions.
5. **Handle dirty state**: Show cancel buttons and disable save buttons based on `form.formState.isDirty`.
5. **Handle dirty state**: Show cancel buttons and disable save buttons based on `form.formState.isDirty`. Make sure you destructure `isDirty` from `form.formState` (see https://react-hook-form.com/docs/useform/formstate)
6. **Error handling**: Always use mutations with `onSuccess` and `onError` callbacks that show toast notifications.
@@ -104,8 +104,13 @@ Studio implementation (preferred in Studio code):
import { DiscardChangesConfirmationDialog } from 'components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog'
import { useConfirmOnClose } from 'hooks/ui/useConfirmOnClose'
const form = useForm(...)
// Always destructure formState values otherwise they won't be updated
// See https://react-hook-form.com/docs/useform/formstate
const { isDirty } = form.formState
const { confirmOnClose, handleOpenChange, modalProps } = useConfirmOnClose({
checkIsDirty: () => form.formState.isDirty,
checkIsDirty: () => isDirty,
onClose,
})