ReactHustle

How to use react-select with Formik (Typescript)

Jasser Mark Arioste

Jasser Mark Arioste

How to use react-select with Formik (Typescript)

The react-select package is a great library to use when building awesome interactive select components. But how do we integrate it with Formik and Typescript?

Here's how I did it.

We'll create a custom component that uses Formik hooks to set the value. We also define the OptionType and GroupedOption so that it'll be easier to manage the types. 

Step 1- Import the Dependencies: #

// components/FormikReactSelect.tsx
import { useField, useFormikContext } from "formik";
import React from "react";
import Select from "react-select";
import { StateManagerProps } from "react-select/dist/declarations/src/useStateManager";
...
123456

Step 2 - Define the Props for Our App #

...
// components/FormikReactSelect.tsx
// define the OptionType for your app
type MyOption = {
  label: string;
  value: any;
};
//define the group option type
type GroupedOption = {
  label: string; // group label
  options: MyOption[];
};
// component props
type Props = {
  name: string;
} & Omit<
  StateManagerProps<MyOption, false | true, GroupedOption>,
  "value" | "onChange"
>;
...
1234567891011121314151617181920

Explanation:

Lines 3-6 & 6-8: We define the OptionType and GroupedOption type:

Line 13-18: We define the component props. The name prop is for the Formik field name.

We intersect it with StateManagerProps from react-select to so that we can still use the props for the Select component.

We omit value and onChange since we're using Formik hooks to set the field value. Formik will manage the state for us.

Step 3 - Define the FormikReactSelect Component #

...
const FormikReactSelect = (props: Props) => {
  const { name, ...restProps } = props;
  const [field] = useField(name);
  const { setFieldValue } = useFormikContext();

  //flatten the options so that it will be easier to find the value
  const flattenedOptions = props.options?.flatMap((o) => {
    const isNotGrouped = "value" in o;
    if (isNotGrouped) {
      return o;
    } else {
      return o.options;
    }
  });

  //get the value using flattenedOptions and field.value
  const value = flattenedOptions?.filter((o) => {
    const isArrayValue = Array.isArray(field.value);

    if (isArrayValue) {
      const values = field.value as Array<any>;
      return values.includes(o.value);
    } else {
      return field.value === o.value;
    }
  });

  return (
    <Select
      {...restProps}
      value={value}
      // onChange implementation
      onChange={(val) => {
        //here I used explicit typing but there maybe a better way to type the value.
        const _val = val as MyOption[] | MyOption;
        const isArray = Array.isArray(_val);
        if (isArray) {
          const values = _val.map((o) => o.value);
          setFieldValue(name, values);
        } else {
          setFieldValue(name, _val.value);
        }
      }}
    />
  );
};

export default FormikReactSelect;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849

Usage #

We have to use the <Formik/> provider component for this technique to work. I recommend to use it since I find it cleaner and more maintainable over the useFormik hook.

// components/HomePage.tsx
import FormikReactSelect from "../components/FormikReactSelect";
import { Form, Formik } from "formik";
export default function HomePage() {
  const options = [
    { value: "chocolate", label: "Chocolate" },
    { value: "strawberry", label: "Strawberry" },
    { value: "vanilla", label: "Vanilla" },
  ];
  return (
    <>
      <Formik
        onSubmit={(values) => console.log(values)}
        initialValues={{
          favoriteFruits: ["chocolate"],
        }}
      >
        <Form>
          <FormikReactSelect
            name="favoriteFruits"
            isMulti={true}
            options={options}
          />
          <button type="submit">Submit</button>
        </Form>
      </Formik>
    </>
  );
}

1234567891011121314151617181920212223242526272829

Explanation:

Lines 5-9: We define the options for the select component:

Line 15: Notice that the initialValues are of type string[] but the options are MyOption[] type. Most production apps doesn't use objects for values. That's we extract the o.value from on the onChange handler from Step 3. 

Code #

The full code can be accessed at jmarioste/react-select-formik-tutorial.

Conclusion #

We learned how to integrate react-select with Formik by using Formik hooks and Formik context provider. 

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle