forms-and-input
Builds react-hook-form + valibot forms: one schema, catalog-key errors, TextField kinds (keyboard, autofill), focus chain.
Skill by zakariaf
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.
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.
About
Use cases: frontend, security
Reviews
Write a reviewNo 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 theFrom zakariaf/ReactNative-Skills, MIT.
More from zakariaf/ReactNative-Skills
- 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
android-release-play
zakariaf
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
architecture-and-boundaries
zakariaf
Places app code in the src/ layout and checks import boundaries: one-way zones, pure core, ports with fakes, one composition root.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
async-safety
zakariaf
Prevents silent async bugs: effect races, stale results, lost promises, leaked listeners and timers, AbortSignal gaps.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
audio-and-haptics-feedback
zakariaf
Builds SFX and haptics behind ports.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
auth-and-secure-storage
zakariaf
Builds sign-in - tokens in expo-secure-store, shared refresh, protected routes, OAuth PKCE, Apple sign-in.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
background-tasks
zakariaf
Builds expo-background-task jobs: idempotent, budgeted runs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9
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
- Repository
- github.com/zakariaf/ReactNative-Skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from zakariaf
- 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9
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.
Cite this page
forms-and-input skill. Agent Mart. https://agentmart.dev/skills/zakariaf-forms-and-input (last updated 2026-10-24)