rn-smart-form
v0.1.0
Published
Lightweight, type-safe form management and automatic keyboard navigation for React Native.
Downloads
130
Maintainers
Readme
rn-smart-form
Lightweight, type-safe form management with automatic keyboard navigation for React Native. Fields register themselves in mount order, the return key walks focus down the form, and the last field submits it.
- Declarative validation rules or a
zodresolver - Validate on blur by default; opt-in
validateOnChange - Async custom validators (stale results are discarded)
- Programmatic control via
useSmartForm() - TypeScript generics flow end-to-end
Installation
npm install rn-smart-form
# or
yarn add rn-smart-formRequires react >= 16.8.6 and react-native >= 0.63.0.
Optional zod support:
npm install zodQuick start
import { SmartFormContainer, SmartInput } from 'rn-smart-form';
interface SignUpValues {
email: string;
password: string;
}
function SignUpScreen() {
return (
<SmartFormContainer<SignUpValues>
initialValues={{ email: '', password: '' }}
onSubmit={async (values) => {
await api.signUp(values); // typed as SignUpValues
}}
>
<SmartInput<SignUpValues>
name="email"
label="Email"
keyboardType="email-address"
rules={{ required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }}
/>
<SmartInput<SignUpValues>
name="password"
label="Password"
secureTextEntry
rules={{ required: true, minLength: 8 }}
/>
{/* Return key on "password" submits the form */}
</SmartFormContainer>
);
}Validation rules
| Rule | Options | Default message |
| ----------- | --------------------------------------------------- | ---------------------------------- |
| required | true / { value, message } / message string | "This field is required." |
| minLength | number / { value, message } | "Minimum N characters required." |
| maxLength | number / { value, message } | "Maximum N characters allowed." |
| pattern | RegExp / { value, message } | "Invalid format." |
| validate | (value, values) => string \| undefined \| Promise | — |
rules={{
required: 'Please enter your username',
validate: async (value) => {
const taken = await api.usernameTaken(value);
return taken ? 'Username is already taken' : undefined;
},
}}Validation timing
- Blur – fields validate when blurred (default). Errors appear once a field is touched.
- Change – a touched (or already-invalid) field revalidates as you type, so errors clear the moment the input becomes valid.
validateOnChange– set on<SmartFormContainer>to also validate and surface errors on every keystroke from the first character.
Zod resolver
import { z } from 'zod';
import { SmartFormContainer, SmartInput, zodResolver } from 'rn-smart-form';
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
<SmartFormContainer resolver={zodResolver(schema)} onSubmit={onSubmit}>
...
</SmartFormContainer>;Resolver errors merge under per-field rule errors.
useSmartForm
Access form state and actions from any child of the container:
import { Text, Pressable } from 'react-native';
import { useSmartForm } from 'rn-smart-form';
function SubmitBar() {
const {
values,
errors,
touched,
activeField,
isValid,
isSubmitting,
setValue,
setError,
setFieldTouched,
resetForm,
submitForm,
focusNextField,
isLastField,
} = useSmartForm<SignUpValues>();
return (
<Pressable disabled={!isValid || isSubmitting} onPress={() => submitForm()}>
<Text>{isSubmitting ? 'Saving…' : 'Sign up'}</Text>
</Pressable>
);
}Calling useSmartForm() outside a <SmartFormContainer> throws a descriptive
error.
Keyboard navigation
- Fields participate in navigation in mount order, including fields that mount/unmount dynamically (conditional steps, sections).
- Return key is
nextfor every field except the last, which becomesdone. Override per field with the standardreturnKeyTypeprop. - Submitting (
submitEditing) on any single-line field focuses the next one; on the last field it submits the form. Multiline inputs never auto-navigate.
Props reference
SmartFormContainer
| Prop | Type | Description |
| ------------------ | ------------------------------ | ------------------------------------------- |
| initialValues | Partial<T> | Starting values, reused by resetForm() |
| onSubmit | (values: T) => void\|Promise | Called after validation passes |
| resolver | (values: T) => FormErrors | Schema-level validation (see zodResolver) |
| validateOnChange | boolean | Revalidate + show errors on every keystroke |
SmartInput
All standard TextInputProps pass through, plus:
| Prop | Description |
| ---------------------------------------- | ------------------------------------------------- |
| name | Field key inside form values (required) |
| label | Rendered above the input |
| rules | Declarative validation rules (see table above) |
| containerStyle | Wrapper view style |
| inputStyle / style | Base TextInput style |
| focusStyle | Extra style while focused |
| errorStyle | Extra style applied when showing an error |
| errorContainerStyle / errorTextStyle | Error text styling |
| showErrorText | Set false to hide the error text (default true) |
Refs forward a handle exposing focus, blur, isFocused, and clear.
Example
An Expo example app lives in example/:
cd example
npm install
npm run ios # or npm run androidDevelopment
npm install
npm test # jest + @testing-library/react-native
npm run typecheck # strict TS across src, tests, and example
npm run lint
npm run build # react-native-builder-bob → lib/