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.
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", ];
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;
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.