---
name: Form Patterns
slug: form-patterns
category: Frontend
description: Form Patterns guides building and refactoring forms in aghub desktop app code. Use it for HeroUI v3 forms, react-hook-form integration, validation, custom editors, and error presentation.
github: "https://github.com/AkaraChen/aghub/tree/main/.agents/skills/project-form-patterns"
language: TypeScript
stars: 264
forks: 17
install: "npx degit https://github.com/AkaraChen/aghub/tree/main/.agents/skills/project-form-patterns ~/.claude/skills/project-form-patterns"
installs_to: ~/.claude/skills/project-form-patterns
source_path: .agents/skills/project-form-patterns/SKILL.md
collection_size: 25
category_size: 600
collection_url: "https://dirskills.com/collections/AkaraChen/aghub"
added: 2026-09-02T05:20:04.205Z
last_synced: 2026-09-02T05:20:04.205Z
canonical_url: "https://dirskills.com/skills/form-patterns"
---

# Form Patterns

Form Patterns guides building and refactoring forms in aghub desktop app code. Use it for HeroUI v3 forms, react-hook-form integration, validation, custom editors, and error presentation.

**Install:**

```bash
npx degit https://github.com/AkaraChen/aghub/tree/main/.agents/skills/project-form-patterns ~/.claude/skills/project-form-patterns
```

## README

# aghub Form Patterns

Follow these rules when building forms in this project.

## Source of Truth

- Check the official docs first, not memory and not bundled/local docs, when form behavior is in question.
- For HeroUI, prefer the official site:
    - `https://v3.heroui.com/docs/react/components/form`
    - `https://v3.heroui.com/docs/react/components/text-field`
    - `https://v3.heroui.com/docs/react/components/field-error`
    - `https://v3.heroui.com/docs/react/components/select`
- Use local skill docs only as a convenience after confirming the official API.

## Default Stack

- Prefer `react-hook-form` for non-trivial forms.
- Use `Controller` for HeroUI `Select` and any custom controlled widgets.
- Keep one form state. Do not create a second derived validation state unless there is a concrete need the form library cannot express.

## Validation Behavior

- When RHF controls validation, set HeroUI form fields to `validationBehavior="aria"`.
- Do not rely on HeroUI/native validation defaults together with RHF. Native validation can steal focus and submission flow while bypassing the error UI you expect from RHF.
- Let RHF own validation rules and submission blocking.

## Error Rendering

- For HeroUI text fields, use:
    - `isInvalid={Boolean(fieldState.error)}`
    - Conditionally render `<FieldError>{fieldState.error.message}</FieldError>` inside the same `TextField`.
- Do not invent unsupported props. In particular, do not assume `TextField` supports an `errorMessage` prop.
- Keep the official anatomy:

```tsx
<TextField isInvalid={Boolean(fieldState.error)} validationBehavior="aria">
	<Label>Name</Label>
	<Input {...inputProps} />
	{fieldState.error && <FieldError>{fieldState.error.message}</FieldError>}
</TextField>
```

- For non-form or custom composite controls, prefer HeroUI `ErrorMessage` instead of hand-rolled error text.
- Use `ErrorMessage` for collection-style or custom editors that are not true form fields, including key/value editors, tag selectors, and similar composite controls.

## Custom Editors

- Custom widgets like `AgentSelector`, `EnvEditor`, `HttpHeaderEditor`, and `KeyPairEditor` should still be registered in RHF through `Controller`.
- For custom editors that manage arrays or compound values, compute validation from the current field value and surface one aggregated error below the editor when possible.
- For that aggregated error, prefer HeroUI `ErrorMessage`.
- Do not inject per-row error UI into tight horizontal layouts unless the design explicitly calls for it.

## Key/Value Editors

- Keep row layout simple:
    - two inputs
    - one delete button
    - no extra wrappers that change flex behavior unless necessary
- Prefer aggregate error text below the whole editor over inline row errors. This avoids breaking spacing and alignment.
- Implement that aggregate error with HeroUI `ErrorMessage`, not a custom `<p>` block.
- If you must show row-level issues, redesign the layout first; do not bolt error blocks into a row that was designed as a single-line control.

## Practical Rules

- Prefer `onPress` for HeroUI buttons.
- Use `type="button"` for non-submit buttons inside forms.
- Preserve existing visual patterns in this repo; do not restyle forms while adding validation.
- After form changes, run `bun run build` in `crates/desktop` when possible and separate unrelated existing build failures from the changes you made.

## Anti-Patterns

- Do not mix RHF validation with a parallel `validationErrors` state for the same fields.
- Do not depend on HeroUI default native validation when you expect RHF errors to drive the UI.
- Do not push validation messages into each key/value row unless you intentionally redesign that editor.
- Do not trust remembered HeroUI APIs for forms without checking the official site first.
