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
Top comments (0)