Formik with Typescript

Simple Form with form global validation and a custom component

Let's code a 'forget your password' form to illustrate this use case. The form will contains a single input field so that users can post their emails:

Formik form

export const ForgotPasswordForm: React.FC<ForgotPasswordFormProps> = ({  isProcessing,setProcessing}) => {

  const validationSchema = Yup.object({
    email: Yup.string()
      .email('your message')
      .required(''your message')
  })

  return (

      <Formik
        initialValues={initialValues}
        validationSchema={validationSchema}
        onSubmit={async values => { /* submission */ }}
      >
        {formik => {
          const { errors, touched, isValid } = formik
          return (
            <Form autoComplete="off">
                <MailInput
                  name="email"
                  label="Your email"
                  placeholder="Your placeholder"
                  errors={errors} //Formik errors object
                  touched={touched} //Formik touched props
                  autofocus={true} //We will set autofocus to true so that the input field is focus by     default when the form is rendered
                  disabled={isProcessing} // the form is controlled isProcessing and setProcessing by a component above the form
                />

                <button 
                  ....
                  disabled={!isValid || isProcessing}
                  name="set-password-submit"
                />
            </Form>
          )
        }}
      </Formik>

  )
}

At these point, there is no difference at all (except for the react functionnal component itself) with using formik in a typescript environment.

But be careful because MailInput is a custom component with many props, so here some knowledge of how it works with typescript can be useful. The props are:

name="email" // it's a string label="Your email" // it's a string placeholder="Your placeholder" // it's a string errors={errors} //Formik errors object touched={touched} //Formik touched props autofocus={true} //it's a boolan disabled={isProcessing} //it's a boolan

Formik errors type

import { FormikErrors } from 'formik'
errors: FormikErrors<{[field: string]: any}>

Formik touched props type

import { FormikTouched } from 'formik'
touched?: FormikTouched<{[field: string]: any}>

Custom components

All in all, here is the MailInput with the extra logic so that it can be autofocused by default. Note that i'm using styled-component here and I'm not providing any details about them since this is not the topic here.

import React, { useRef, useEffect } from 'react'

import {
  Field,
  FieldProps,
  ErrorMessage,
  FormikErrors,
  FormikTouched
} from 'formik'

interface InputProps {
  label: string
  name: string
  placeholder?: string
  errors?: FormikErrors<{
    [field: string]: any
  }>
  touched?: FormikTouched<{
    [field: string]: any
  }>
  autofocus?: boolean
  disabled?: boolean
}

export const MailInput: React.FC<InputProps> = ({
  name,
  label,
  placeholder,
  errors,
  touched,
  autofocus = false,
  disabled = false,
}) => {
  const isError = errors[name] && touched[name]
  const errorClass = isError ? 'error' : ''

  if (isValid) isValid(!isError)
  const inputRef = useRef<HTMLInputElement>(null)

  useEffect(() => {
    if (inputRef.current) {
      const ref = inputRef.current
      if (autofocus) {
        ref.focus()
      }
    }
  }, [inputRef, autofocus])

  return (
    <>
      <Label htmlFor={name}>{label}</Label>
      <Field name={name} validate={validate}>
        {({ meta }: FieldProps) => {
          return (
            <>
              <Field
                innerRef={inputRef}
                className={errorClass}
                id={name}
                name={name}
                placeholder={placeholder || ''}
                autoComplete="off"
                disabled={disabled}
              />
              <span></span>
            </>
          )
        }}
      </Field>
    </>
  )
}