Skip to content
Agentmart
Submit a tool

forms-and-input

Builds react-hook-form + valibot forms: one schema, catalog-key errors, TextField kinds (keyboard, autofill), focus chain.

Skill by zakariaf

IndexedMIT★ 11
Write a review

Chalk Labs

Need help deploying forms-and-input?

Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.

Book a call

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/zakariaf-forms-and-input

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Builds react-hook-form + valibot forms: one schema, catalog-key errors, TextField kinds (keyboard, autofill), focus chain. Use when adding a form, text field or validation, or the keyboard hides a field. Not for sign-in (auth-and-secure-storage).

Use cases: frontend, security

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Node.js package

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/forms-and-input

What's inside

Skill name
forms-and-input
Description
Builds react-hook-form + valibot forms: one schema, catalog-key errors, TextField kinds (keyboard, autofill), focus chain. Use when adding a form, text field or validation, or the keyboard hides a field. Not for sign-in (auth-and-secure-storage).
License
MIT
Instructions
223 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts

Files (93)

  • SKILL.md
  • component-library-entries.json · assets/
  • shared.json · assets/
  • account-fields.ts · examples/
  • list-name-availability.ts · examples/
  • root-stack-keyboard-provider.tsx · examples/
  • server-field-errors.ts · examples/
  • async-checks-and-server-errors.md · references/
  • checks-and-tests.md · references/
  • fields-and-autofill.md · references/
  • keyboard-focus-and-submit.md · references/
  • schemas-and-messages.md · references/
  • check-forms.mjs · scripts/
  • check-lib.mjs · scripts/
  • source-scan.mjs · scripts/lib/
  • selftest.mjs · scripts/
  • react-native-keyboard-controller.ts · templates/__mocks__/
  • latin-digits.test.ts · templates/src/core/text/
  • latin-digits.ts · templates/src/core/text/
  • task-editor-schema.test.ts · templates/src/features/task-editor/
  • task-editor-schema.ts · templates/src/features/task-editor/
  • task-editor-screen.test.tsx · templates/src/features/task-editor/
  • task-editor-screen.tsx · templates/src/features/task-editor/
  • use-task-editor-model.test.tsx · templates/src/features/task-editor/
  • use-task-editor-model.ts · templates/src/features/task-editor/
  • field-message-key.test.ts · templates/src/hooks/
  • field-message-key.ts · templates/src/hooks/
  • use-async-field-check.test.ts · templates/src/hooks/
  • use-async-field-check.ts · templates/src/hooks/
  • use-done-bar-space.test.ts · templates/src/hooks/
  • use-done-bar-space.ts · templates/src/hooks/
  • use-text-field.test.tsx · templates/src/hooks/
  • use-text-field.ts · templates/src/hooks/
  • ar.json · templates/src/i18n/catalogs/
  • en.json · templates/src/i18n/catalogs/
  • field-kinds.test.ts · templates/src/ui/form/
  • field-kinds.ts · templates/src/ui/form/
  • field-note.test.tsx · templates/src/ui/form/
  • field-note.tsx · templates/src/ui/form/
  • keyboard-done-bar.test.tsx · templates/src/ui/form/
  • keyboard-done-bar.tsx · templates/src/ui/form/
  • text-field.android.test.tsx · templates/src/ui/form/
  • text-field.test.tsx · templates/src/ui/form/
  • text-field.tsx · templates/src/ui/form/
  • i18n-form-wrapper.tsx · templates/test/forms/
  • package.json · tests/bases/app/
  • root-stack.tsx · tests/bases/app/src/features/root-navigator/
  • languages.ts · tests/bases/app/src/i18n/
  • tsconfig.json · tests/bases/app/
  • CASES.json · tests/fixtures/
  • SUITE.json · tests/fixtures/
  • EXPECT.txt · tests/fixtures/bad-controller-rules/
  • EXPECT.txt · tests/fixtures/bad-error-message-key/
  • EXPECT.txt · tests/fixtures/bad-error-message-translated/
  • EXPECT.txt · tests/fixtures/bad-field-on-change/
  • EXPECT.txt · tests/fixtures/bad-field-ref/
  • EXPECT.txt · tests/fixtures/bad-field-spread/
  • EXPECT.txt · tests/fixtures/bad-focus-error/
  • EXPECT.txt · tests/fixtures/bad-form-mode/
  • EXPECT.txt · tests/fixtures/bad-form-resolver/
  • …and 33 more

SKILL.md (first lines)

# Forms and input

Makes every form in an Expo app one valibot schema behind react-hook-form, whose every message is a
catalog key, rendered with one labelled `TextField` whose kind sets the keyboard, autofill and
capitalization, chained by the return key, kept above the keyboard on iOS and Android, and checked
asynchronously only outside the schema. One zero-dependency checker proves it on the app's source.

## Rules that must hold

1. **One valibot schema per form, behind `valibotResolver`.** `useForm<Input, unknown, Output>({
   resolver: valibotResolver(schema) })`; the field types are `v.InferInput`, the submitted draft is
   `v.InferOutput`; no `rules` on a Controller (`form-resolver`). Why: one definition gives the type,
   the runtime check and every message, so a field can never validate one way and save another.
2. **Every message is a catalog key, translated at the field.** Each validation action carries a
   literal key of the source catalog (`v.nonEmpty('task-editor.title.required')`,
   `schema-message-key`); `setError` passes a key too (`error-message-key`); `fieldMessageKey` turns
   anything else into the generic key. Why: an action without a message shows valibot's English
   default ("Invalid length: Expected !0 but received 0") in every language.
3. **The schema is synchronous and Hermes-safe.** No `*Async` API in a form schema (`schema-async`);
   no graphemes or words actions unless `Intl.Segmenter` is polyfilled (`schema-segmenter`). Why: the
   resolver runs the whole schema on every validation, with no pause, cancel or deadline, and Hermes
   has no `Intl.Segmenter` (it throws on the phone while Jest on Node passes).
4. **An async check ("is this name free?") runs in `useAsyncFieldCheck`, never in the schema.** It
   starts only for a valid shape, waits for typing to pause, lets the newest value win, has a
   deadline, stops on unmount, and says which value it answers; submit needs a pass for exactly the
   value sent, and the server still judges at write time (a refusal becomes `setError` with a key).
   Why: a check per keystroke floods the server and an old answer can land on a newer value.
5. **Digits in any script are read.** A schema that matches `\d` or calls `Number()` transforms the
   text with `toLatinDigits` first, or, for a decimal, money or quantity field, with
   value-objects-money-and-units' `normalizeDecimalInput` (`latin-digits`). Why: Arabic, Persian and
   Devanagari keyboards, and the iOS pads in those languages, type ٣٠ or ۳۰ (and `٫` or `,` for the
   decimal point), which `/^\d+$/` and `Number()` refuse.
6. **Screens render `TextField`, never a raw `TextInput`** (`raw-text-input`). `TextInput` lives only
   in the field folder (`src/ui/form/`), whose components join the component catalogue. Why: label,
   error, hint, live region, keyboard and autofill are solved once; a raw input forgets one of them.
7. **The visible label is the input's accessible name; the error is never colour alone.** The label
   sits above the input; on iOS the input carries it as `accessibilityLabel` (the label view hidden
   from VoiceOver), on Android it points at the label view with `accessibilityLabelledBy` (an
   EditText's content description loses to its typed text). The error or hint is the input's
   `accessibilityHint` and sits in a polite live region, and an invalid field gets a bold edge and a
   sentence (`input-label`). Why: a placeholder vanishes when typing starts and is not read as a
   name, and about 1 in 12 men cannot tell the error colour apart.
8. **A field kind sets the keyboard, autofill, capitalization, autocorrect and secure entry
   together** (`FIELD_KINDS`; a raw input that hides text sets `autoComplete`, an email keyboard sets
   `autoCapitalize`: `input-preset`). Why: a password without `autoComplete` gets no password manager
   on either platform, and an email field that capitalizes breaks every address.
9. **Fields bind the React Native way: `onChangeText`, `onBlur`, `ref`** (`useTextField`; never
   `onChange={field.onChange}`, `{...field}` or `register`: `field-on-change`; the ref is passed on:
   `field-ref`). Why: react-hook-form reads `event.target.value`, which a React Native change event
   lacks, so the field would hold `undefined`; without the ref nothing can focus the field.
10. **Errors wait for the first blur, and a refused submit goes to the first problem.** `mode:
    'onTouched'` (`form-mode`), `shouldFocusError` left on (`focus-error`), and the refusal is
    announced. Why: `onChange` mode shouts "invalid email" at the first letter; focusing the field
    moves VoiceOver and TalkBack there too.
11. **The return key walks the form.** A single-line field names the next field (`enterKeyHint`
    `next`, `submitBehavior` `submit`, `setFocus`), the last one submits (`done`), a multiline field
    makes new lines. Why: closing and reopening the keyboard between fields flickers and loses the
    user's place.
12. **The keyboard never hides the field or swallows a tap.** `KeyboardProvider` wraps the root
    (`keyboard-provider`); forms scroll in `KeyboardAwareScrollView` (`keyboard-aware-scroll`) with
    `keyboardShouldPersistTaps="handled"` (`keyboard-persist-taps`); a feature with a number,
    decimal or phone pad renders `KeyboardDoneBar` (`pad-done-bar`), and the scroll view keeps the
    focused field above the bar (`useDoneBarSpace`: `bottomOffset` = the bar's measured height plus
    a gap). Why: Android's edge-to-edge window no longer resizes for the keyboard, the first tap on
    Save would only close it, the iOS pads have no return key, and the bar sits on the keyboard,
    over whatever the scroll view keeps just above it.
13. **Submit is one void intent that never loses input.** The model calls `handleSubmit(...)()` and
    catches its promise, shows `isSubmitting` (from `useFormState`) as busy, resets the form only
    after the save returned `ok` and `handleSubmit` has settled, and keeps every typed character on
    a failure. Why: `handleSubmit` rethrows what the save throws, a reset before the write is data
    loss, and a reset inside the save is undone (the form stays "submitted" and the next draft
    shows errors from its first keystroke).

## Workflow

1. **Look first.** From the repo root run `node ${CLAUDE_SKILL_DIR}/scripts/check-forms.mjs .`.
   NOT APPLICABLE means the app has no form yet; otherwise the problems are the work list. Read
   [references/checks-and-tests.md](references/checks-and-tests.md) for each rule's fix.
2. **Prerequisites.** These come from their owner skills and must exist first: the theme, `AppText`
   and `PressableSurface` (design-system-structure), `t()`/`useT`, `MessageKey` and the catalogs
   (i18n-strings-and-catalogs), `useServices` and the error log (architecture-and-boundaries),
   `Result` (error-handling-typed-results), the latest-request, `delayOrAbort` and `withTimeout`
   helpers (async-safety), `useAnnounce`/`useReduceMotion` (accessibility-as-code), the render
   helpers (unit-and-component-tests). Install, verified 2026-10-03 (re-verify:
   `npm view react-hook-form version`): `npx expo install react-native-keyboard-controller`
   (1.21.9, in the SDK 57 map, included in Expo Go) and
   `npm install -E react-hook-form@7.89.0 @hookform/resolvers@5.9.1 valibot@1.5.0`.
3. **Copy the field kit** from `templates/` to the path on each file's first line: `src/ui/form/`
   (`field-kinds.ts`, `text-field.tsx`, `field-note.tsx`, `keyboard-done-bar.tsx`), `src/hooks/`
   (`use-text-field.ts`, `field-message-key.ts`, `use-async-field-check.ts`,
   `use-done-bar-space.ts`),
   `src/core/text/latin-digits.ts`, `test/forms/i18n-form-wrapper.tsx`, each with its test; merge
   the `forms.*` keys of `templates/src/i18n/catalogs/*.json` into every catalog and translate them.
   `__mocks__/react-native-keyboard-controller.ts` is the canonical root mock (keep it as is).
4. **Wire the root.** Wrap the root stack in `KeyboardProvider`, inside `GestureHandlerRootView`
   and outside the motion and navigation-theme providers
   ([examples/root-stack-keyboard-provider.tsx](examples/root-stack-keyboard-provider.tsx)). The
   canonical `root-stack.tsx` of app-startup-and-bootstrap already renders it: check it is there; in an
   older root, add the import and the element and keep the file's header (the `device-only` marker).
5. **List the field components.** If `contracts/component-library.json` does not exist yet, create
   it from ui-component-library's catalogue template first (with `src/ui/form/` in place, that
   skill's `check-components` fails `catalogue-missing` without it), then add the three entries of
   `assets/component-library-entries.json`.
6. **Write the schema first, with its test.** Copy the `task-editor` templates as the pattern and
   rename them: bounds from the data layer, a key per message, `toLatinDigits` before digits
   (`normalizeDecimalInput` for a decimal, money or quantity field), local
   duplicate checks through a factory. Read
   [references/schemas-and-messages.md](references/schemas-and-messages.md).
7. **Write the model hook**: `useForm` with the resolver and `mode: 'onTouched'`, one `useTextField`
   per field with its kind and `next`/`onSubmit`, the submit intent, labels translated
   ([references/fields-and-autofill.md](references/fields-and-autofill.md),
   [references/keyboard-focus-and-submit.md](references/keyboard-focus-and-submit.md)).
8. **Add an async check only when the answer lives elsewhere** (a server, another store): a
   `FieldCheck` port, `useAsyncFieldCheck`, the status and error on the field, submit gated on
   `hasPassed(value)` ([references/async-checks-and-server-errors.md](references/async-checks-and-server-errors.md),
   [examples/list-name-availability.ts](examples/list-name-availability.ts)).
9. **Write the screen** as a pure view: `KeyboardAwareScrollView` with
   `keyboardShouldPersistTaps="handled"`, one `TextField` per field spread from the model, the save
   failure in a live region, `KeyboardDoneBar` when a pad kind is used, its `onHeightChange` and the

From zakariaf/ReactNative-Skills, MIT.

More from zakariaf/ReactNative-Skills

Signals

Stars
11
Forks
2
Open issues
0
Last commit (this folder)
Oct 17, 2026 (13 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from zakariaf

Done for you

Rather not set it up yourself?

Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.

See how we build AI agents →Read the case studies

Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.

launches shipped
50+
launches shipped
media placements
500+
media placements
founders on every account
2
founders on every account

Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block

Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.

Get forms-and-input running in your business

Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying forms-and-input?

CallGet help
Cite this page

forms-and-input skill. Agent Mart. https://agentmart.dev/skills/zakariaf-forms-and-input (last updated 2026-10-24)