Crafting Dynamic Forms in React with Form Atoms: A Comprehensive Guide
Introduction
In this guide, we’ll explore how to create forms using the form-atoms
library. We’ll start with simple forms and gradually move to more complex forms, including lists. This guide will cover the following:
Setting up the environment
Creating a simple form
Adding validation
Creating a list form
Advanced form with nested lists
1. Setting Up the Environment
First, let’s set up our project. Ensure you have Node.js installed, then create a new project and install the necessary dependencies.
npx create-react-app form-atoms-demo
cd form-atoms-demo
npm install jotai form-atoms @form-atoms/field @form-atoms/list-atom zod
2. Creating a Simple Form
We’ll start by creating a simple form with text and number fields.
Step 1: Define the Form Fields
Create a new file PersonForm.js
and define the form fields using @form-atoms/field
.
import { textField, numberField } from "@form-atoms/field";
import { formAtom, useForm, InputField } from "form-atoms";
const personForm = formAtom({
name: textField(),
age: numberField(),
});
export const PersonForm = () => {
const { fieldAtoms, submit } = useForm(personForm);
return (
<form onSubmit={submit(console.log)}>
<InputField atom={fieldAtoms.name} label="Your Name" />
<InputField atom={fieldAtoms.age} label="Your Age" />
<button type="submit">Submit</button>
</form>
);
};
Step 2: Use the Form in Your App
Import and use the PersonForm
component in your App.js
.
import React from "react";
import { PersonForm } from "./PersonForm";
function App() {
return (
<div className="App">
<h1>Simple Form</h1>
<PersonForm />
</div>
);
}
export default App;
3. Adding Validation
To add validation, we use the zod
schema. Let’s add some basic validations to our form fields.
Step 1: Update the Form Fields
Update the PersonForm.js
to include validations.
import { z } from "zod";
const personForm = formAtom({
name: textField({ schema: (s) => s.min(1, "Name is required") }),
age: numberField({ schema: (s) => s.min(18, "Must be at least 18") }),
});
Step 2: Display Validation Errors
Update the PersonForm
component to display validation errors.
export const PersonForm = () => {
const { fieldAtoms, submit, errors } = useForm(personForm);
return (
<form onSubmit={submit(console.log)}>
<InputField atom={fieldAtoms.name} label="Your Name" />
{errors.name && <span>{errors.name}</span>}
<InputField atom={fieldAtoms.age} label="Your Age" />
{errors.age && <span>{errors.age}</span>}
<button type="submit">Submit</button>
</form>
);
};
4. Creating a List Form
Now, let’s create a more complex form that includes a list of items.
Step 1: Define the List Fields
Create a new file EnvironmentForm.js
and define the list fields using @form-atoms/list-atom
.
import { fieldAtom, formAtom, useForm, InputField } from "form-atoms";
import { listAtom, List } from "@form-atoms/list-atom";
const environmentVariables = listAtom({
name: "environment",
value: [{ key: "GITHUB_SECRET", value: "<hash>" }],
fields: ({ key, value }) => ({
key: fieldAtom({ value: key }),
value: fieldAtom({ value }),
}),
});
const form = formAtom({ environmentVariables });
export const EnvironmentForm = () => {
const { submit } = useForm(form);
return (
<form onSubmit={submit(console.log)}>
<List atom={environmentVariables}>
{({ fields }) => (
<>
<InputField atom={fields.key} label="Variable Key" />
<InputField atom={fields.value} label="Variable Value" />
</>
)}
</List>
<button type="submit">Submit</button>
</form>
);
};
Step 2: Use the List Form in Your App
Import and use the EnvironmentForm
component in your App.js
.
import React from "react";
import { EnvironmentForm } from "./EnvironmentForm";
function App() {
return (
<div className="App">
<h1>Environment Variables Form</h1>
<EnvironmentForm />
</div>
);
}
export default App;
5. Advanced Form with Nested Lists
Finally, let’s create an advanced form with nested lists.
Step 1: Define the Nested List Fields
Create a new file ProjectForm.js
and define the nested list fields.
import { fieldAtom, formAtom, useForm, InputField } from "form-atoms";
import { listAtom, List } from "@form-atoms/list-atom";
const projectForm = formAtom({
projectName: fieldAtom(),
teamMembers: listAtom({
name: "teamMembers",
value: [{ name: "Alice", tasks: [{ title: "Task 1" }] }],
fields: ({ name, tasks }) => ({
name: fieldAtom({ value: name }),
tasks: listAtom({
name: "tasks",
value: tasks,
fields: ({ title }) => ({
title: fieldAtom({ value: title }),
}),
}),
}),
}),
});
export const ProjectForm = () => {
const { submit } = useForm(projectForm);
return (
<form onSubmit={submit(console.log)}>
<InputField atom={projectForm.projectName} label="Project Name" />
<List atom={projectForm.teamMembers}>
{({ fields }) => (
<>
<InputField atom={fields.name} label="Team Member Name" />
<List atom={fields.tasks}>
{({ fields }) => (
<InputField atom={fields.title} label="Task Title" />
)}
</List>
</>
)}
</List>
<button type="submit">Submit</button>
</form>
);
};
Step 2: Use the Nested List Form in Your App
Import and use the ProjectForm
component in your App.js
.
import React from "react";
import { ProjectForm } from "./ProjectForm";
function App() {
return (
<div className="App">
<h1>Project Form</h1>
<ProjectForm />
</div>
);
}
export default App;
Conclusion
In this guide, we’ve covered the basics of creating forms using form-atoms
. We started with a simple form, added validation, created a list form, and finally built an advanced form with nested lists. The form-atoms
library provides a flexible and powerful way to manage form state in React applications.
Feel free to explore more features and customize your forms as needed. Happy coding!
Subscribe to my newsletter
Read articles from Pawan Gangwani directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Pawan Gangwani
Pawan Gangwani
I’m Pawan Gangwani, a passionate Full Stack Developer with over 12 years of experience in web development. Currently serving as a Lead Software Engineer at Lowes India, I specialize in modern web applications, particularly in React and performance optimization. I’m dedicated to best practices in coding, testing, and Agile methodologies. Outside of work, I enjoy table tennis, exploring new cuisines, and spending quality time with my family.