DEV Community

loading...
Cover image for Unform: the definitive form library for React apps

Unform: the definitive form library for React apps

italomlp profile image Italo Menezes ・Updated on ・6 min read

All of us, React developers - or even javascript developers that already heard about React ecosystem - know a thing: forms in React application are really painful and verbose (like Formik's devs said too).

We have Redux Forms, Formik and many other libraries to make forms happen in React. But the first not convince me... Why? I don't agree in keep form state inside Redux (could we continue friends?). And the second is very powerful... But (nothing matters if there is a "but" after it) by being very powerful, it becomes very verbose or complex for simple forms (and there is a problem with performance in big forms too, subject to another post). And most of these libraries work with controlled components for input fields. It does not always be the better thing to include in your project if you are going to work with big forms and/or need much performance.

What can we do so? Well, because of these points, a lifestyle/company/community called Rocketseat, located in Brazil, makes Unform:

GitHub logo Rocketseat / unform

Easy peasy highly scalable ReactJS & React Native forms! 🚀

Unform

Easy peasy highly scalable ReactJS & React Native forms! 🚀

npm Coverage Status

Overview

Unform is a performance focused library that helps you creating beautiful forms in ReactJS & React Native with the power of uncontrolled components performance and React Hooks.

Wanna test Unform before using it?

Not available with React Native Web or Expo Web, use the iOS/Android devices in Expo Snack

Contributing

Thank you for being interested on making this package better. We encourage everyone to help improving this project with some new features, bug fixes and performance issues. Please take a little bit of your time to read our guides, so this process can be faster and easier.

Contribution Guidelines

Take a moment to read about our Contribution Guidelines so you can understand how to submit an issue, commit and create pull requests.

Code of Conduct

We expect you…

Let's see it in action!

You can view the source code of this example in https://github.com/italomlp/unform-example

1. What we will need?

2. Init a react app

yarn create react-app form-example

or

npx create-react-app form-example

or another way described in the create-react-app repository

You will see the following result:
create-react-app result

Then we can run our project with:

cd form-example/
yarn start

And see the following in browser:
create-react-app start

3. Clean src folder and Install dependencies

After creating the app, we will have this folder structure:
react-app folder structure

First, I'll remove src/App.test.js, src/App.css, src/logo.svg and src/serviceWorker.js files, that we will not use in this post.

rm src/App.test.js src/App.css src/logo.svg src/serviceWorker.js

We need then to install our dependencies. The yup is a good choice for validation purposes (and is the recommendation from unform's team). And the react-datepicker is an awesome component to demonstrate the integration between Unform and third libs.

yarn add @rocketseat/unform yup react-datepicker

So, we can start coding.

4. Make the form

If you look to your browser, see that the app does not compile anymore. To avoid this, we have to change our src/index.js to:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

And our src/App.js to:

import React from 'react';

function App() {
  return (
    <div>
      <h1>Contact form</h1>
    </div>
  );
}

export default App;

The use of Unform is very simple: we import things and use them in our JSX. Let's see.

// ...
import { Input, Form } from "@rocketseat/unform";

function App() {
  return (
    <div>
      <h1>Contact form</h1>
      <Form>
        <Input name="fullname" label="Full name" />
        <Input name="phone" label="Phone" />
        <Input name="email" label="Email" />

        <button type="submit">Save</button>
      </Form>
    </div>
  );
}

This works well...

But how can we get the data?

Simple: pass a handle submit function to Form component.

// ...
const handleSubmit = data => {
    console.log(data);
};

return (
  <div>
    <h1>Contact form</h1>
    <Form onSubmit={handleSubmit}>
    {/* ... */}

Nice. The result is:
unform working

But, if I want nested fields?

Cool, just use the Scope component to wrap nested fields. As you can see, I added address fields like this:

import { Input, Form, Scope } from "@rocketseat/unform"; // added Scope component import

// ...
<Form onSubmit={handleSubmit} >
  {/* other Input components */}

  <Scope path="address">
    <Input name="city" label="City" />
    <Input name="state" label="State" />
  </Scope>
  {/* ... */}
</Form>

The Scope component is used to tell to React: "hey, my children are properties of an object which I represent". And will resut in:
unform scope

And if I want to populate the form with initial values?

You can. Just pass an object to initialData prop of Form component:

// ...
const initialData = {
  fullname: "Italo Menezes",
  phone: "999999999",
  email: "italo@email.com",
  address: {
    city: "Rio de Janeiro",
    state: "Rio de Janeiro"
  }
};

// ...

<Form onSubmit={handleSubmit} initialData={initialData}>
  {/* ... */}
</Form>

Ok ok. But validation sometimes is a pain. How can I make it with Unform?

For validation, we integrate with Yup, that is simply an object schema validator. Don't get me wrong when I say "simply". It is very powerful. What I mean is that power is not always the same that complex. So, let's see.

With object schema validations, we can declare rules to object properties, making them strings, with minimum or maximum length, matching a Regex, and so on.

We will validate our form by these rules:

  • Full name has to have at least 5 chars. Required.
  • The phone number has to be only numeric and have exactly 9 digits. Required.
  • The email has to be in an email format (so obvious). Not required.
  • The city is not required.
  • The state is not required.

So, with Yup, let's create this object schema:

const schema = Yup.object().shape({
  fullname: Yup.string()
    .min(5, "The FULL name is only this?")
    .required("Full name is required."),
  phone: Yup.string()
    .matches(/^[0-9]{9}$/g, "Is this a phone number?")
    .required("Phone is required."),
  email: Yup.string().email("Is this an email?"),
  address: Yup.object().shape({
    city: Yup.string().notRequired(),
    state: Yup.string().notRequired()
  })
});

And add it to schema prop in Form component:

<Form onSubmit={handleSubmit} schema={schema}>
  {/* ... */}
</Form>

Stop! Let's add a simple CSS. This project is becoming very ugly!

Replace the code of src/index.css with:

body {
  padding: 10px;
}

input {
  display: block;
  margin-bottom: 10px;
}

label {
  display: block;
}

span {
  display: block;
  font-size: 10px;
  color: red;
  margin-bottom: 15px;
}

Coming back to validations...

If you run this now and click on save without values on inputs, you will get this:
yup validations

Ok. All fine till now. And if I need to use my own input field in the form?

Well, the Rocketseat devs thought about this too, and make a hook to use with other components called useField.

We will use the react-datepicker that was added to our project in the beginning. To do this, we need to wrap it and add the useField like this:

import React, { useEffect, useState, useRef } from "react"; // add the hooks

import { Input, Form, Scope, useField } from "@rocketseat/unform"; // useField hook
import * as Yup from "yup";

import ReactDatepicker from "react-datepicker"; // react datepicker component
import "react-datepicker/dist/react-datepicker.css"; // react datepicker css

// ...

const Datepicker = ({ name, label }) => {
  const ref = useRef(null); // for ref manipulation purposes
  const { fieldName, registerField, defaultValue, error } = useField(name); // the name of the prop in form object is used here
  const [selectedDate, setSelectedDate] = useState(defaultValue); // the state of our datepicker component

  useEffect(() => {
    registerField({ // here, we're registering the field in the whole form
      name: fieldName,
      ref: ref.current,
      path: "props.selected", // this is the path to selected date in ReactDatepicker (wich is the selected prop)
      clearValue: pickerRef => { // for reset purposes
        pickerRef.clear();
      }
    });
  }, [fieldName]);

  return (
    <>
      {/* the label is like label in Unform Input component */}
      {!!label && <label htmlFor="datepicker">{label}</label>}
      <ReactDatepicker
        id="datepicker"
        name={fieldName}
        selected={selectedDate}
        onChange={date => setSelectedDate(date)}
        ref={ref}
      />
      {/* the error is like error in Unform Input component */}
      {error && <span>{error}</span>}
    </>
  );
};

// ...
  <Form onSubmit={handleSubmit} schema={schema}>
    {/* ... */}
    <Datepicker name="birthDate" label="Birth date" />
    {/* ... */} 
  </Form>

Well, I added comments in the code, hope you understand.

So, this will result in this:
with datepicker
result with datepicker

If you are not familiar with React hooks, I recommend one reading:
Hooks at a glance: https://reactjs.org/docs/hooks-overview.html

And finally, if I want to reset values after submit?

The onSubmit function of Form has a second parameter which is an object. This object has (till the date of this post was wrote) only one property helper, called resetForm. We can use it like this:

const handleSubmit = (data, { resetForm }) => {
  console.log(data);
  resetForm();
};

We finished here. You can see more examples and docs in the Unform repository on Github. Unform is in its initial releases, but has an awesome community involved and the best devs I've never meet.

If you like this post, share and give it a ❤️. Also, you can follow me on Github and social media. Thanks for reading, and see you later!

Discussion

pic
Editor guide
Collapse
dinsmoredesign profile image
Derek D

Finally, a React Form package that isn't stupidly complex for no reason. I've been toying with the idea lately to utilize React in some of our larger apps that currently use Vue, as it's concepts of immutability would help reason about our complex data flow a bit better... But forms are a nightmare in React and, since most of our apps have a strong CRUD aspect, converting to React on these areas would suck. I'll definitely keep an eye out on Unform, it looks great!

Collapse
italomlp profile image
Italo Menezes Author

Yeah! The Unform was designed to keep it simple. And even be powerful (the devs are thinking about so many good features to add). It's really worth it.

Collapse
sarneeh profile image
Jakub Sarnowski

As I fell in love with styled-components, the lack of support of it in unform is a no for me right now. But I see they have it in their roadmap so I'll definitely try it out :D

Collapse
italomlp profile image
Italo Menezes Author

Yeah. There is already a pull request adding styled components feature. Stay tuned for future versions.

Collapse
jaffparker profile image
Jaff Parker

Have you heard of Informed? It's another simple React form library (previously called react-forms), but has some mature features since it's older. If you look at it, what would you say is the difference between you and them?

Collapse
italomlp profile image
Italo Menezes Author

Well, I didn't know Informed. I saw it now, and seems good too. But, things that I realized: 1) Unform hits 1k stars in three days; 2) the company behind Unform (Rocketseat) has great developers, I know them, they teach programming and has a big community involved, with about 59k of students, suggesting features and contributing; 3) Unform hits 100% of tests coverage (since Informed has 78%); 4) Unform was designed with performance in mind too, not only simplicity. So, don't get me wrong, Informed seems to be a good choice too. But I really liked Unform and its support. Feel free to try it out too and choose the best to your needs. And thanks for your comment! :D

Collapse
bluebill1049 profile image
Bill

Thanks for sharing Unform, I have built a custom hook for form validation. Please check it out when u have sometimes as well :) react-hook-form.com/

Collapse
chiangs profile image
Stephen E. Chiang

Forms is probably one of the things I preferred in angular when I moved over to react... But this looks interesting, thanks for sharing it!

Collapse
italomlp profile image
Italo Menezes Author

We share this thought. But maybe Unform breaks the wheel.