Facebook Sign Up Form Tutorial | React Binden💪👑 x Tailwindcss ️
Tags: javascript, webdev, beginners, react
Published: 2021-11-02 18:29:31

[React](https://github.com/facebook/react) is an awesome FrontEnd UI library created by Facebook. But forms in React always been a little hard. This is what the library [react-binden](https://react-binden.netlify.app/) solves. It's a fairly new form handling library for React. It's extremely easy to learn & use

> Get a deep dive on [React Binden](https://dev.to/krtirtho/react-forms-made-easy-with-react-binden-1aeb) or visit the [Docs](https://react-binden.netlify.app/)
> 

[Tailwindcss](https://tailwindcss.com/) is my most favorite css framework❤️ & by far the most awesome library I've ever found

> This article only shows how to build a Signup form using React, react-binden & tailwindcss. I'm assuming one will be familiar with above tools & technologies
> 

## What are we Building?

We're making a simple, regular & boring old Sign Up Form inspired by Facebook's Sign Up Form with React, react-binden & tailwindcss. But there's a twist. The form will still be a Sign Up Form but **we'll be honest** for the placeholders, labels & license agreement etc.. texts🙃😆

## Creating the project

For bootstraping the project, we'll use [vite](https://vitejs.dev/). An extraordinary frontend build tool that is super fast & also supports various frontend frameworks

Initiating the project

```bash
$ npm init vite
```

It'll ask a few questions, including project name & which frontend framework to use. Write the name of your choice & select the `react` option

Now open the project into VSCode/your favorite code editor. Then in the terminal, inside the project root run

```bash
$ npm install
```

Then remove all the non required files e.g `src/App.css`, `src/logo.svg`. Remove the all the boilerplate code inside `src/App.jsx`

Now install the following dependencies:

```bash
$ npm install react-binden tailwindcss postcss autoprefixer nanoid clsx
```

 Now run the following command to initiate TailwindCSS inside your project

```bash
$ npx tailwindcss init -p
```

This will create the following files `tailwind.config.js`, `postcss.config.js`

Now add the following to `src/index.css`

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

Let's enable [JIT](https://tailwindcss.com/docs/just-in-time-mode) (Just in Time) mode for the tailwindcss compiler. Add `mode: "jit"` inside the code `tailwind.config.js`'s export config object. Then the file should look like below:

```jsx
module.exports = {
  // added jit mode
  mode: "jit",
  // purge Array
  purge: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  darkMode: "class", // or 'media' for automatic dark mode detection
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
```

Now run following command to start the vite dev server

```bash
$ npm run dev
```

## Custom Themed Input

Now that we've done initializing the project, it's time to create a awesome & beautiful `Input` component with our favorite tailwindcss

Create a file as `src/ModInput.jsx` then do the following

```jsx
import { Input } from 'react-binden'
import clsx from "clsx"
import { nanoid } from "nanoid"

function ModInput(props) {
  const id = props.id ?? nanoid()

  return (
    <div>
      {props.model.error && <p>{props.model.error}</p>}
      <Input id={id} {...props} />
      <label htmlFor={id}>{props.label}</label>
    </div>
  )
}

export default ModInput
```

May be you're thinking *why did I put the label & the error hint in the wrong order*. Well, there's a reason. But now, let's style these components. I'll be using [clsx](https://github.com/lukeed/clsx) for handling multiple & conditional classes efficiently

```jsx
import { Input } from 'react-binden'
import clsx from "clsx"
import { nanoid } from "nanoid"

function ModInput(props) {
  const inputStyle = clsx(
    props.className,
    "peer transition-all p-1 border-2 border-solid rounded outline-none",
    {
      // conditional classes
      ["border-red-400"]: props.model.touched && !!props.model.error,
      ["border-gray-500 focus:border-blue-400"]: !props.model.error
    },
  )

  const id = props.id ?? nanoid()

  // radio & checkboxes are different than text fields thus they need
  // a bit differently adjusted styles
  const rowTypes = ["checkbox", "radio"]
  const secondDivStyles = clsx(
    "inline-flex",
    // corrects the wrong order of label & error-hint
    !rowTypes.includes(props.type) ? "flex-col-reverse" : "flex-row items-center"
  )

  const labelStyles = clsx(
    "transition-all select-none peer-focus:text-blue-500 font-semibold",
    { 
			["font-normal peer-focus:text-black ml-2"]: rowTypes.includes(props.type),
      ["peer-focus:text-red-500"]: props.model.touched && !!props.model.error 
    }
  )

  return (
    <div className={secondDivStyles}>
      {props.model.error && (
                <p className="text-red-500 text-sm ml-2 group-focus">
                   {props.model.error}
                </p>)
      }
      <Input id={id} className={inputStyle} {...props} />
      <label htmlFor={id} className={labelStyles}>{props.label}</label>
    </div>
  )
}

export default ModInput
```

Now, let's answer why the order of error-hint & label are in reverse in the JSX. This is because of tailwind's `peer` class & `peer-focus:` prefix/variant. TailwindCSS provides an awesome way to handle css's styles based on sibling's state. `peer` prefix works as the CSS's `+` operator for selectors. But `peer` only works when the upper most element/sibling has the `peer` class. Downwards siblings can use upward siblings states but not vice-versa

> Learn more about [TailwindCSS Sibling Select Variant](https://blog.tailwindcss.com/tailwindcss-2-2)
> 

## Basic Form

Let's use the newly created `ModInput`. Now in `src/App.jsx` we've to create our basic form using react-binden's `Form`, `useModel` & `regex`. We'll style the form later. Now only focus on Logic

```jsx
import { Form, regex, useModel } from "react-binden"
import ModInput from "./ModInput"

function App() {
  // models of each field
  const email = useModel("")
  const password = useModel("")
  const confirmPassword = useModel("")
  const username = useModel("")
  const birthday = useModel("")
  // since we're using radio-group a common name for all the
  // radio-button is required to make it function
  const gender = useModel("", { name: "gender", required: true })

  function handleSubmit(_e, _states, { setSubmitting, resetForm }) {
      // resetting the form
			setInterval(() => {
		      resetForm();
		      setSubmitting(false);
		    }, 500);
  }

  return (
    <div>
      <h1>Honest Facebook Sign Up</h1>
      <p><b>Disclaimer!:</b> This is just a parody of Facebook. Nothing related to actual Facebook corp. Made just for fun & entertainment</p>
      <Form onSubmit={handleSubmit}>
        <ModInput
          model={username}
          label="Username"
          // only allows lowercase letters
          pattern={[/^[a-z]+$/, "only lower case name is allowed"]}
          required
        />
        <ModInput
          type="email"
          label="Email"
          model={email}
          pattern={[regex.email, "Should be a valid email"]}
          required
        />
        <ModInput
          type="password"
          label="Password"
          model={password}
          pattern={[regex.moderatePassword, "Write a stronger password"]}
          required
        />
        <ModInput
          type="password"
          model={confirmPassword}
          imprint-model={password}
          label="Confirm Password"
          required
        />
        <ModInput
          type="datetime"
          label="Birthday"
          model={birthday}
          pattern={[regex.date_dd_MM_yyyy, "should follow the `ddmmyy` format"]}
          required
        />
        <div>
          <p>Gender</p>
          <div>
            <ModInput
              type="radio"
              model={gender}
              value="male"
              label="Male"
            />
            <ModInput
              type="radio"
              model={gender}
              value="female"
              label="Female"
            />
            <ModInput
              type="radio"
              model={gender}
              value="other"
              label="Other"
            />
          </div>
        </div>
        <div>
          <button type="submit">Get Ruined</button>
        </div>
      </Form>
    </div>
  )
}

export default App
```

> If you feel overwhelmed with all of the code above you can learn about react-binden [here](https://react-binden.netlify.app/)
> 

Now, that we've all the fields Facebook requires to Signup, let's style & structure them as following

```jsx
// ... import stuffs
function App() {
  // ... other stuff (models, handlers etc..)
  return (
    <div className="flex flex-col items-center">
      <h1 className="m-2 text-3xl text-center font-bold">
        Honest Facebook Sign Up
      </h1>
      <p className="text-center">
        <b>Disclaimer!:</b> This is just a parody of Facebook. Nothing related
        actual Facebook corp. Made just for fun & entertainment
      </p>
      <Form
        className="inline-flex flex-col p-5 space-y-2 max-w-xl"
        onSubmit={handleSubmit}
      >
        <div>
          <h2 className="text-2xl text-gray-900 font-semibold">Sign Up</h2>
          <p className="text-xs text-gray-600">
            It's quick & easy
          </p>
        </div>
        <hr />
        <ModInput
          model={username}
          label="Username"
          pattern={[/^[a-z]+$/, "only lower case name is allowed"]}
          required
        />
        <ModInput
          type="email"
          label="Email"
          model={email}
          pattern={[regex.email, "Should be a valid email"]}
          required
        />
        <div className="flex space-x-5">
          <ModInput
            type="password"
            label="Password"
            model={password}
            pattern={[regex.moderatePassword, "Write a stronger password"]}
            required
          />
          <ModInput
            type="password"
            model={confirmPassword}
            imprint-model={password}
            label="Confirm Password"
            required
          />
        </div>
        <ModInput
          type="datetime"
          model={birthday}
          pattern={[regex.date_dd_MM_yyyy, "should follow the `ddmmyy` format"]}
          required
        />
        <div>
          <p className="font-bold">Gender</p>
          <div className="flex items-center justify-between w-1/2">
            <ModInput type="radio" model={gender} value="male" label="Male" />
            <ModInput
              type="radio"
              model={gender}
              value="female"
              label="Female"
            />
            <ModInput type="radio" model={gender} value="other" label="Other" />
          </div>
        </div>
        <p className="text-gray-600 text-xs pb-5">
          By clicking Sign Up, you agree to our Terms, Data Policy and Cookie Policy. You may receive SMS notifications from us and can opt out at any time.
        </p>
        <div className="flex justify-center">
          <button
            type="submit"
            className="bg-[#00a400] py-2 px-10 text-white font-bold rounded"
          >
            Get Ruined
          </button>
        </div>
      </Form>
    </div>
  );
}

export default App;
```

## The Fun Part

I hope it'd now look visually appealing but that's boring. Nothing fun & interesting. Of course, I could add awesome animations, weirdest scroll effect or various CSS animations. But we're developers & we do hard work "occasionally"🤥. So let's use our "joke power" (which I obviously don't have but still trying) with texts. Let's just pretend we're actual Facebook developer & we, for some reason have to be slightly honest with what we build🙃

FUN GENERATING

```jsx
import { Form, regex, useModel } from "react-binden";
import ModInput from "./ModInput";

function App() {
  const email = useModel("");
  const password = useModel("");
  const confirmPassword = useModel("");
  const username = useModel("");
  const birthday = useModel("");
  const gender = useModel("", { name: "gender", required: true });

  function handleSubmit(_e, { errors }, { setSubmitting, resetForm }) {
    setInterval(() => {
      resetForm();
      setSubmitting(false);
    }, 500);
  }

  return (
    <div className="flex flex-col items-center">
      <h1 className="m-2 text-3xl text-center font-bold">
        Honest Facebook Sign Up
      </h1>
      <p className="text-center">
        <b>Disclaimer!:</b> This is just a parody of Facebook. Nothing related
        actual Facebook corp. Made just for fun & entertainment
      </p>
      <Form
        className="inline-flex flex-col p-5 space-y-2 max-w-xl"
        onSubmit={handleSubmit}
      >
        <div>
          <h2 className="text-2xl text-gray-900 font-semibold">Sign Up</h2>
          <p className="text-xs text-gray-600">
            It's quick & easy (profit for us)
          </p>
        </div>
        <hr />
        <ModInput
          model={username}
          label="Username"
          placeholder="Credit Card Pin. Oops, Username"
          pattern={[/^[a-z]+$/, "only lower case name is allowed"]}
          required
        />
        <ModInput
          type="email"
          label="Email"
          model={email}
          pattern={[regex.email, "Should be a valid email"]}
          placeholder="Password. Oh sorry, Email"
          required
        />
        <div className="flex space-x-5">
          <ModInput
            type="password"
            label="Password"
            model={password}
            pattern={[regex.moderatePassword, "Write a stronger password"]}
            placeholder="Why not use, Hail Zuckerberg?"
            required
          />
          <ModInput
            type="password"
            model={confirmPassword}
            imprint-model={password}
            label="Confirm Password"
            placeholder="Isn't it, Hail Zuckerberg?"
            required
          />
        </div>
        <ModInput
          type="datetime"
          label="Birthday (Makes it easier for your friends to beg treats from you)"
          model={birthday}
          pattern={[regex.date_dd_MM_yyyy, "should follow the `ddmmyy` format"]}
          required
        />
        <div>
          <p className="font-bold">Gender</p>
          <div className="flex items-center justify-between w-1/2">
            <ModInput type="radio" model={gender} value="male" label="Male" />
            <ModInput
              type="radio"
              model={gender}
              value="female"
              label="Female"
            />
            <ModInput type="radio" model={gender} value="other" label="Other" />
          </div>
        </div>
        <p className="text-gray-600 text-xs pb-5">
          By clicking Get Ruined, you agree that you're our product, we can do
          whatever we want with & we own you (for free). You may receive SMS
          notifications from us and can opt out at any time (not actually).
        </p>
        <div className="flex justify-center">
          <button
            type="submit"
            className="bg-[#00a400] py-2 px-10 text-white font-bold rounded"
          >
            Get Ruined
          </button>
        </div>
      </Form>
    </div>
  );
}

export default App;
```

Glad that it's finished. For a moment, it was feeling like it'd never end. But don't go way. There's a catch in the project. I created the entire website *without taking care of responsiveness*.  So you can now make it responsive by yourself. Do this as a home-work

## Results

After writing 2 million lines (200 actually) of code we're finally done. Let's see what have we built so far & let's hope there's no bug

{% codesandbox facebook-signup-parody-5l21l %}

Source Code: https://github.com/KRTirtho/fb-parody-signup

## Social

Follow me on [twitter](https://twitter.com/KrTirtho)

Follow me on [Reddit](https://www.reddit.com/user/krtirtho)

Give **react-binden** a [⭐ on Github](https://github.com/KRTirtho/react-binden)