Skip to main content

Front-End Development

Advanced Formik Techniques for Building Dynamic and Scalable Forms

Programer Codes On A Laptop Connected To Additional Screen

Formik is an excellent tool for managing forms in React, simplifying state management and validation. However, as forms become more complex, you may need to dive into some advanced techniques. In this post, we’ll cover how to handle dynamic fields, create custom validations, integrate with third-party UI libraries, and optimize performance.

1. Dynamically Adding and Removing Fields

Sometimes, the number of form fields you need depends on user input. For instance, a survey form might allow users to add or remove questions dynamically.

Formik has a FieldArray component that makes this easy. Here’s how to use it:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Formik, Form, Field, FieldArray } from "formik";
const DynamicForm = () =>{
return(
<Formik
initialValues={{ questions: [""]}}
onSubmit={(values) => console.log(values)}>
{({ values }) =>(
<Form>
<FieldArray name="questions">
{({ push, remove }) =>(
<div>
{values.questions.map((_, index) =>(
<div key={index}>
<Field
name={`questions[${index}]`}
placeholder="Enter question"
/>
<button type="button" onClick={() =>remove(index)}>Remove</button>
</div>
))}
<button type="button" onClick={() =>push("")}>Add Question</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
};
export default DynamicForm;
import { Formik, Form, Field, FieldArray } from "formik"; const DynamicForm = () => { return ( <Formik initialValues={{ questions: [""] }} onSubmit={(values) => console.log(values)}> {({ values }) => ( <Form> <FieldArray name="questions"> {({ push, remove }) => ( <div> {values.questions.map((_, index) => ( <div key={index}> <Field name={`questions[${index}]`} placeholder="Enter question" /> <button type="button" onClick={() => remove(index)}>Remove</button> </div> ))} <button type="button" onClick={() => push("")}>Add Question</button> </div> )} </FieldArray> <button type="submit">Submit</button> </Form> )} </Formik> ); }; export default DynamicForm;
import { Formik, Form, Field, FieldArray } from "formik";
const DynamicForm = () => {
return (
<Formik
initialValues={{ questions: [""] }}
onSubmit={(values) => console.log(values)}>
{({ values }) => (
<Form>
<FieldArray name="questions">
{({ push, remove }) => (
<div>
{values.questions.map((_, index) => (
<div key={index}>
<Field
name={`questions[${index}]`}
placeholder="Enter question"
/>
<button type="button" onClick={() => remove(index)}>Remove</button>
</div>
))}
<button type="button" onClick={() => push("")}>Add Question</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
};
export default DynamicForm;

In this example, users can add and remove questions from the form. push adds a new question, and remove deletes an existing one.

 

2. Custom Validation

Formik’s built-in validation is great, but sometimes you may need to add your own logic, like checking if a username is available.

Here’s an example of how to do that:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const validateUsername = async(value) =>{
let error;
if(!value){
error = 'Username is required';
}else{
const isAvailable = await checkUsernameAvailability(value); // Assume an API call
if(!isAvailable){
error = 'Username is already taken';
}
}
return error;
};
<Formik initialValues={{ username: ''}} onSubmit={(values) => console.log(values)}>
<Form>
<label htmlFor="username">Username</label>
<Field name="username" validate={validateUsername} />
<ErrorMessage name="username" component="div" />
<button type="submit">Submit</button>
</Form>
</Formik>
const validateUsername = async (value) => { let error; if (!value) { error = 'Username is required'; } else { const isAvailable = await checkUsernameAvailability(value); // Assume an API call if (!isAvailable) { error = 'Username is already taken'; } } return error; }; <Formik initialValues={{ username: '' }} onSubmit={(values) => console.log(values)} > <Form> <label htmlFor="username">Username</label> <Field name="username" validate={validateUsername} /> <ErrorMessage name="username" component="div" /> <button type="submit">Submit</button> </Form> </Formik>
const validateUsername = async (value) => {
let error;
if (!value) {
error = 'Username is required';
} else {
const isAvailable = await checkUsernameAvailability(value); // Assume an API call
if (!isAvailable) {
error = 'Username is already taken';
}
}
return error;
};

<Formik initialValues={{ username: '' }} onSubmit={(values) => console.log(values)} > 
<Form>
<label htmlFor="username">Username</label> 
<Field name="username" validate={validateUsername} />
<ErrorMessage name="username" component="div" />
<button type="submit">Submit</button> 
</Form>
</Formik>

In this example, the validateUsername function checks if the username is taken and displays an error message accordingly.

 

3. Integrating with Third-Party UI Libraries

Formik works well with third-party libraries like Material-UI. You can pass custom components to Formik using the as prop or by using the render function.

Here’s how you can use Material-UI’s TextField component with Formik:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import TextField from '@mui/material/TextField';
<Formik
initialValues={{ email: ""}}
onSubmit={(values) => console.log(values)}>
<Form>
<Field
name="email"
as={TextField}
label="Email"
variant="outlined"
fullWidth
/>
<button type="submit">Submit</button>
</Form>
</Formik>
import TextField from '@mui/material/TextField'; <Formik initialValues={{ email: "" }} onSubmit={(values) => console.log(values)}> <Form> <Field name="email" as={TextField} label="Email" variant="outlined" fullWidth /> <button type="submit">Submit</button> </Form> </Formik>
import TextField from '@mui/material/TextField';

<Formik
initialValues={{ email: "" }}
onSubmit={(values) => console.log(values)}>
<Form>
<Field
name="email"
as={TextField}
label="Email"
variant="outlined"
fullWidth
/>
<button type="submit">Submit</button>
</Form>
</Formik>

Alternatively, you can use the render prop for even more control:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Field name="email">
{({ field, meta }) =>(
<TextField
{...field}
label="Email"
variant="outlined"
fullWidth
error={meta.touched&&Boolean(meta.error)}
helperText={meta.touched&& meta.error}
/>
)}
</Field>
<Field name="email"> {({ field, meta }) => ( <TextField {...field} label="Email" variant="outlined" fullWidth error={meta.touched && Boolean(meta.error)} helperText={meta.touched && meta.error} /> )} </Field>
<Field name="email">
{({ field, meta }) => (
<TextField
{...field}
label="Email"
variant="outlined"
fullWidth
error={meta.touched && Boolean(meta.error)}
helperText={meta.touched && meta.error}
/>
)}
</Field>

This allows you to use your favorite UI components without losing the benefits of Formik’s form handling.

 

4. Optimizing Performance

When working with large forms, performance can become an issue. You can minimize unnecessary re-renders and make your form more efficient by using these techniques:

Use React.memo: This prevents components from re-rendering unnecessarily.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const OptimizedField = React.memo(({ name, type }) =>(
<Field name={name} type={type} />
));
const OptimizedField = React.memo(({ name, type }) => ( <Field name={name} type={type} /> ));
const OptimizedField = React.memo(({ name, type }) => (
<Field name={name} type={type} />
));

Validate on Blur: You can reduce the number of validation checks by only validating when the user leaves a field (blur event). This is especially useful in large forms.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Formik
initialValues={{ email: ''}}
validateOnChange={false}
validateOnBlur={true}
onSubmit={(values) => console.log(values)}
>
{/* Form Fields */}
</Formik>
<Formik initialValues={{ email: '' }} validateOnChange={false} validateOnBlur={true} onSubmit={(values) => console.log(values)} > {/* Form Fields */} </Formik>
<Formik
initialValues={{ email: '' }}
validateOnChange={false}
validateOnBlur={true}
onSubmit={(values) => console.log(values)}
>
{/* Form Fields */}
</Formik>

## For More deeper Information, check out the official [Formik Field Arrays documentation].

 

Conclusion

With these advanced Formik techniques, you can build dynamic, scalable forms with custom validation and optimize performance in larger applications. Give them a try in your next project, and see how much easier handling forms in React can be!

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Rizwan Sheikh, Technical Consultant

I’m Rizwan Sheikh, an Adobe Certified Expert Frontend Developer with a passion for technology, music, and sports. I specialize in delivering innovative web development solutions to clients across various industries. Outside of coding, I enjoy playing first-person shooter games and staying active with soccer and running. My love for tech, music, and sports keeps me motivated to consistently produce high-quality, creative work.

More from this Author

Follow Us