Undergoing a meaningful dialogue on the web requires a number of forms.
While the most of the attention has been reasonably placed on certain aspects such as validation, it’d be relatively easy to improve the form where it is closest to the user — its design.
Doing this will not only make our lives (as developers) easier, but also our users’. It’s a win-win. One way to do this is to format form inputs in real time to model its real-life equivalent. One tool that enables us do this is Cleave.js.
To paraphrase, Cleave.js helps you format your <input/>
value as you type. That’s it.
This tutorial is about how to use the Cleave.js library with React. You can also check the GitHub page to see how to use it in other ways.
Cleave.js allows you to do 6 types of formatting:
Instead of showing you how all the format types work in isolation, I’ve built a simple makeshift donation form:
As annotated, we’ll be touching on:
1 → Credit card number/type formatting
2 → Date formatting
3 → Digit formatting
4 → Numeral formatting
5 → Phone number formatting
6 → Custom formatting (with prefix, blocks, and delimiters).
To get started, I’ve created a CodeSandbox and installed the Cleave.js package.
The first step is to import Cleave.js:
import Cleave from "cleave.js/react";
Then, we use it instead of an <input/>
:
<Cleave placeholder="Enter credit card number" className="form-field" />
The <Cleave/>
component returns an <input/>
form element with the appropriate type (we don’t need to specify or bother about what type it returns). For its configuration, it takes an options
props, which is a config for the different kind of formatting that can be done.
const [creditCardNo, setCreditCardNo] = useState(""); function onCreditCardChange(e) { setCreditCardNo(e.target.rawValue); } <Cleave placeholder="Enter credit card number" options={{ creditCard: true, }} onChange={e => onCreditCardChange(e)} className="form-field" />
With the creditCard
property to true
and an onChange
event handler. The creditCardNo
state gets updated by accessing the formatted input value with e.target.rawValue
.
This alone formats the input as the user types.
However, what would be fun is to be proactive and show them the kind of credit card provider the digits correspond to.
To do this, we pass in the onCreditCardTypeChanged
event handler to the options
property.
const [creditCardNo, setCreditCardNo] = useState(""); const [creditCardType, setCreditCardType] = useState(""); function onCreditCardTypeChanged(type) { setCreditCardType(type); } function onCreditCardChange(e) { setCreditCardNo(e.target.rawValue); } <Cleave placeholder="Enter credit card number" options={{ creditCard: true, onCreditCardTypeChanged }} onChange={e => onCreditCardChange(e)} className="form-field" />
Unlike the
onChange
property on the<Cleave/>
component,onCreditCardTypeChanged
event handler is added as a property ofoptions
and accessed through thetype
(type
is only a name, you can name it whatever you like).
const [creditCardExpiryDate, setCreditCardExpiryDate] = useState(""); function onCreditCardExpiryChange(e) { setCreditCardExpiryDate(e.target.rawValue); } <Cleave placeholder="MM/YY" options={{ date: true, datePattern: ["m", "d"] }} onChange={onCreditCardExpiryChange} className="form-field" />
We’ve switched the options
prop to have the type of date
set to true and we’re formatting with a datePattern
similar to that of credit cards, showing only the month and day.
While there are other ways to enforce the three digits maximum for CVVs, cleave also offers an indirect way to do this. With blocks, you can pre-define the maximum length an input can be, and how many blocks. This is represented in an array.
For example, a block of [2]
will make sure the user can only type two characters. Using this knowledge, we can cleave our CVV input as:
const [cvv, setCVV] = useState(""); function onCVVChange(e) { setCVV(e.target.rawValue); } <Cleave placeholder="CVV" options={{ blocks: [3], numericOnly: true }} onChange={onCVVChange} className="form-field" />
This allows for a single block of characters with a maximum of three digits, which we enforced with numericOnly
set to true
.
Our credit card details formatting should give this result:
const [donationAmount, setDonationAmount] = useState(""); function onDonationAmountChange(e) { setDonationAmount(e.target.rawValue); } <Cleave placeholder="0.00" options={{ numeral: true, numeralThousandsGroupStyle: "thousand" }} onChange={onDonationAmountChange} className="form-field" />
To format our donation amount, we set the numeral
property to true
and also set numeral formatting to thousand with numeralThousandsGroupStyle:
"thousand"
.
Keep in mind, this is an indirect way to format “currencies” as formatting currencies is locale dependent. Cleave formats numerals not currencies.
Phone number Formatting
This is a little different than the others. To begin with, you need to import the locale/country, in this case, Nigeria, before using it.
import "cleave.js/dist/addons/cleave-phone.ng"; const [phoneNumber, setPhoneNumber] = useState(""); function onPhoneChange(e) { setPhoneRawValue(e.target.rawValue); } <Cleave placeholder="0908 765 4321" options={{ phone: true, phoneRegionCode: "NG" }} onChange={onPhoneChange} className="form-field" />
Here, the phone
property is set to true, and the phoneRegionCode
is set to “NG”.
If you find the phone number formatting a little different from what you intend, Cleave also provides a custom formatting – check the next format.
Imagine you require your users to enter cryptic 2FA passphrases. Cleave can help with its custom formatting:
const [customValue, setCustomValue] = useState(""); function onCustomValueChange(e) { setCustomRawValue(e.target.rawValue); } <Cleave placeholder="KEY-2DJDH2-3I37X-2MXHGX" options={{ prefix: "KEY", blocks: [3, 6, 5, 6], delimiter: "—", numericOnly: false }} onChange={onCustomValueChange} className="form-field" />
Some of the options you can pass here is the prefix
, blocks
, delimiter
(for the blocks), numericOnly
etc.
This will ensure a formatting of 3, 6, 5, and 6 digits, separated with the delimiter value, the first blocks item will be for the prefix:
It is important to always remember that Cleave.js is meant for formatting not validation, so it customizes — not enforce — the input values. Before submission, make sure to validate your forms.
When you try to submit the form you do not need to worry about how what goes in the state
or how the values gets transformed because Cleave.js strips all formatting away and gives you the raw value. If I try to submit this form:
I get the values in my state
:
creditCardNo: "4242424242424242" phoneRawValue: "09087363636" dateRawValue: "1222" donationAmount: "450000" customRawValue: "KEY27272bbc6262gbxy2" cvv: "222"
Having users fill out forms in this way is no doubt an interesting and credible way to go and will put us a step ahead.
Check out the links below for more.
1. Codesandbox link to full example
2. CleaveJS official website
3. CleaveJS GitHub project
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.