TutorialsCourses
Course Menu
Formik for Beginners

A Primer on Custom Fields

Introduction

Formik doesn't work with just normal form fields, you can build and interpret any value that you've stored on the values. Everything is stored in memory so when developing custom fields you can do anything you want especially when it comes to building custom fields. For example, we're going to build a color picker. Maybe you want to generate 10 random colors a user can choose from. We can create any custom interaction we want and store it inside of our Formik values.

Setup

We're going to use the useField hook. For ease of rendering we'll just choose 10 random colors to loop over for our user to choose.

import { Formik, Form, useField } from "formik";

const COLORS = [
  "#206c7c",
  "#aa1c99",
  "#a94498",
  "#2fc57f",
  "#4ac6c7",
  "#ca9c01",
  "#1a376f",
  "#8db794",
];

The App

This is no different than any classic Formik setup. We'll set a default of color in our initialValues and then render our soon to be custom field component called CustomColorPicker.

function App() {
  const handleSubmit = (values) => {
    console.log(values);
  };

  return (
    <div className="h-screen flex items-center justify-center flex-col bg-gray-100">
      <Formik
        initialValues={{
          color: "",
        }}
        onSubmit={handleSubmit}
      >
        {() => {
          return (
            <Form className="bg-white w-6/12 shadow-md rounded px-8 pt-6 pb-8">
              <CustomColorPicker />
              <div className="flex items-center justify-between mt-4">
                <button
                  className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                  type="submit"
                >
                  Register
                </button>
              </div>
            </Form>
          );
        }}
      </Formik>
    </div>
  );
}

export default App;

Custom Color Picker

First we'll specify our field which we named color

const [field, meta, helpers] = useField("color");

Next we loop over it all the colors. We grab the value and check if the value for the field is equal to the color we're currently looping over. This gives us the ability to highlight the selected color.

return (
  <div className="flex space-x-2">
    {COLORS.map((color) => {
      const isSelected = color === field.value;
      return null;
    })}
  </div>
);

Now we render our square, and use our isSelected value to decide if our border should be black or transparent. The other handy feature of the useField hook is the helper methods that are provided. In general field props are great for interacting with normal html inputs, however they rely on an event. The helper methods have an option to setValue which will just update the value with whatever you pass it.

return (
  <div
    onClick={() => {
      helpers.setValue(color);
    }}
    style={{
      backgroundColor: color,
      cursor: "pointer",
      width: "40px",
      height: "40px",
      border: `4px solid ${isSelected ? "#000" : "transparent"}`,
    }}
  />
);

All put together our final component looks like this.

const COLORS = [
  "#206c7c",
  "#aa1c99",
  "#a94498",
  "#2fc57f",
  "#4ac6c7",
  "#ca9c01",
  "#1a376f",
  "#8db794",
];

const CustomColorPicker = () => {
  const [field, meta, helpers] = useField("color");
  return (
    <div className="flex space-x-2">
      {COLORS.map((color) => {
        const isSelected = color === field.value;

        return (
          <div
            onClick={() => {
              helpers.setValue(color);
            }}
            style={{
              backgroundColor: color,
              cursor: "pointer",
              width: "40px",
              height: "40px",
              border: `4px solid ${isSelected ? "#000" : "transparent"}`,
            }}
          />
        );
      })}
    </div>
  );
};

From here you can take your component in any direction you want. You have access to all the meta fields to display errors, and do whatever you need to for your custom components.