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 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 or visit the Docs
Tailwindcss 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. An extraordinary frontend build tool that is super fast & also supports various frontend frameworks
Initiating the project
$ 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
$ 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:
$ npm install react-binden tailwindcss postcss autoprefixer nanoid clsx
Now run the following command to initiate TailwindCSS inside your project
$ npx tailwindcss init -p
This will create the following files tailwind.config.js
, postcss.config.js
Now add the following to src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Let's enable JIT (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:
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
$ 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
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 for handling multiple & conditional classes efficiently
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
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
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
Now, that we've all the fields Facebook requires to Signup, let's style & structure them as following
// ... 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
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
Source Code: https://github.com/KRTirtho/fb-parody-signup
Social
Follow me on twitter
Follow me on Reddit
Give react-binden a ⭐ on Github