Introduction

React Native doesn’t have a native <form> element, and managing the state of multiple inputs can be verbose. That’s why I’ll show you a simple way to handle forms using Formik and Yup. Once you get the hang of it, you’ll be able to build more complex forms with ease.

Prerequisites

  • Expo CLI installed (docs)
  • Basic React Native knowledge
  • Wrap your app with React Native Paper’s <PaperProvider> (docs)

Setup

npx create-expo-app my-formik-form
cd my-formik-form
npm install formik react-native-paper yup

How does Formik with React Native work

1. Import dependencies

app/components/MyForm.tsx

import { Formik } from "formik"
import { View } from "react-native"
import { TextInput, Text, Button, HelperText } from "react-native-paper"
import * as Yup from "yup"

2. Wrap your form with Formik

Since React Native doesn’t have a native <form>, wrap your component with <Formik>.
app/components/MyForm.tsx

export default function MyForm() {
  return (
    <Formik {...requiredProps}>
      {/* form goes here */}
    </Formik>
  )
}

3. Add necessary props

Formik needs at least two props: initialValues and onSubmit.

<Formik
  initialValues={{
    email: "",
    password: ""
  }}
  onSubmit={values => console.log(values)}
>
  {/* rest of the form */}
</Formik>

4. Use Formik’s callback

Formik exposes several helper methods that make your form work:

<Formik {...requiredProps}>
  {({
    handleChange,
    handleBlur,
    handleSubmit,
    values
  }) => (
    <View>
      {/* email input */}
      <TextInput
        value={values.email}
        onChangeText={handleChange("email")}
        onBlur={handleBlur("email")}
      />

      {/* password input */}
      <TextInput
        secureTextEntry
        value={values.password}
        onChangeText={handleChange("password")}
        onBlur={handleBlur("password")}
      />

      <Button onPress={handleSubmit}>
        Login/Register
      </Button>
    </View>
  )}
</Formik>

You’ve got your first working form!

⚠️ But:

  • There are no labels: users won’t know what to enter.

  • There’s no validation: users don’t get any feedback.

“Is that my name or should I enter my credit card number?”


Adding Labels

Add the label prop to each <TextInput />.

<TextInput
  label="Email" // ✅ add this
  value={values.email}
  onChangeText={handleChange("email")}
  onBlur={handleBlur("email")}
/>

<TextInput
  label="Password" // ✅ add this
  secureTextEntry
  value={values.password}
  onChangeText={handleChange("password")}
  onBlur={handleBlur("password")}
/>

Adding Validation with Yup

To validate user input, we’ll use Yup, a JavaScript schema builder for value parsing and validation.
It works great with Formik to define field requirements like required inputs, length, format, and patterns.

Create validation schema

const SignupSchema = Yup.object().shape({
  email: Yup.string()
    .email("Invalid email.")
    .required("Required"),
  password: Yup.string()
    .min(6, "Too Short")
    .max(30, "Too Long")
    .matches(
      /^(?=.*[A-Za-z])(?=.*\d).+$/,
      "Password must contain at least one letter and one number"
    )
    .required("Required")
})

Add schema to Formik

<Formik
  validationSchema={SignupSchema} // ✅ add this
  initialValues={{
    email: "",
    password: ""
  }}
  onSubmit={values => console.log(values)}
>
  {/* rest of the form */}
</Formik>

Add validation props

<Formik {...requiredProps}>
  {({
    handleChange,
    handleBlur,
    handleSubmit,
    values,
    errors,      // ✅ add
    touched      // ✅ add
  }) => (
    <View>
      {/* inputs go here */}
    </View>
  )}
</Formik>

Add error props to inputs

<TextInput
  label="Email"
  value={values.email}
  onChangeText={handleChange("email")}
  onBlur={handleBlur("email")}
  error={!!(errors.email && touched.email)} // ✅ add
/>

<TextInput
  label="Password"
  secureTextEntry
  value={values.password}
  onChangeText={handleChange("password")}
  onBlur={handleBlur("password")}
  error={!!(errors.password && touched.password)} // ✅ add
/>

Show error messages

HelperText from React Native Paper displays helpful messages below inputs, like validation errors or hints.
It only shows when the visible prop is true.

<TextInput
  label="Email"
  value={values.email}
  onChangeText={handleChange("email")}
  onBlur={handleBlur("email")}
  error={!!(errors.email && touched.email)}
/>
{/* add */}
<HelperText
  type="error"
  visible={!!(errors.email && touched.email)}
>
  {errors.email}
</HelperText>

<TextInput
  label="Password"
  secureTextEntry
  value={values.password}
  onChangeText={handleChange("password")}
  onBlur={handleBlur("password")}
  error={!!(errors.password && touched.password)}
/>
{/* add */}
<HelperText
  type="error"
  visible={!!(errors.password && touched.password)}
>
  {errors.password}
</HelperText>

✅ Full Code

import { Formik } from "formik"
import * as Yup from "yup"
import { View } from "react-native"
import { TextInput, Button, HelperText } from "react-native-paper"

const SignupSchema = Yup.object().shape({
  email: Yup.string()
    .email("Invalid email.")
    .required("Required"),
  password: Yup.string()
    .min(6, "Too Short")
    .max(30, "Too Long")
    .matches(
      /^(?=.*[A-Za-z])(?=.*\d).+$/,
      "Password must contain at least one letter and one number"
    )
    .required("Required")
})

export default function MyForm() {
  return (
    <Formik
      initialValues={{ email: "", password: "" }}
      validationSchema={SignupSchema}
      onSubmit={values => console.log(values)}
    >
      {({
        handleChange,
        handleBlur,
        handleSubmit,
        values,
        errors,
        touched
      }) => (
        <View style={{ padding: 20 }}>
          {/* Email */}
          <TextInput
            label="Email"
            value={values.email}
            onChangeText={handleChange("email")}
            onBlur={handleBlur("email")}
            error={!!(errors.email && touched.email)}
          />
          <HelperText
            type="error"
            visible={!!(errors.email && touched.email)}
          >
            {errors.email}
          </HelperText>

          {/* Password */}
          <TextInput
            label="Password"
            secureTextEntry
            value={values.password}
            onChangeText={handleChange("password")}
            onBlur={handleBlur("password")}
            error={!!(errors.password && touched.password)}
          />
          <HelperText
            type="error"
            visible={!!(errors.password && touched.password)}
          >
            {errors.password}
          </HelperText>

          {/* Submit Button */}
          <Button mode="contained" onPress={handleSubmit}>
            Login / Register
          </Button>
        </View>
      )}
    </Formik>
  )
}

🎯 Conclusion

  • Formik simplifies form state management

  • Yup provides a powerful validation system

  • ✅ With just a few props, you can build clean, user-friendly forms

You’re now ready to create complex forms with ease! 🧠✨