I looked around for Javascript libs that do automatic input formatting for credit card inputs.

The first one was formatter.js which looked promising but it weighs over 6Kb minified and also, when you apply it the placeholder attribute you have on the input disappears.

So, in true software engineering fashion I wrote my own:

function cc_format(value) {
  var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '')
  var matches = v.match(/\d{4,16}/g);
  var match = matches && matches[0] || ''
  var parts = []
  for (i=0, len=match.length; i<len; i+=4) {
    parts.push(match.substring(i, i+4))
  if (parts.length) {
    return parts.join(' ')
  } else {
    return value

And some tests to prove it:

assert(cc_format('1234') === '1234')
assert(cc_format('123456') === '1234 56')
assert(cc_format('123456789') === '1234 5678 9')
assert(cc_format('') === '')
assert(cc_format('1234 1234 5') === '1234 1234 5')
assert(cc_format('1234 a 1234x 5') === '1234 1234 5')

Check out the Demo


Post your own comment
tulio serpio

when i type numbers, the fifth char inserts a space, but the caret places in the fourth position. the sixth char places incorrectly.

just my two cents

Peter Bengtsson

I don't understand. What's not working? The fifth input should be preceded by space. I don't see anything there being incorrect.

tulio serpio

when I type in my desktop your demo works Ok.
But, in my celular (htc sense with firefox beta, with hardware keyboard) I have to type FN + number.
When I type '12345' the caret moves to the begining of the '5', so when I type '6' the result is '1234 65'

Peter Bengtsson

I see. That's frustrating.
I'll talk to the people who work on that and try to figure out if it's a bug in Firefox or a bug in my code.

tulio serpio

I would suggest you apply the formatting on lost focus, because the interaction with the caret is a very hard task. try this on desktop:

1. type "12345678" (the field shows '1234 5678' )
2. place the caret at the begining of field
3. type "12345678" (the field shows '1123 4567 8234 5678' , when I expect '1234 5678' 1234 5678' )

As you can see, the caret changes after the 2nd '1', going to the end of input. This is an unexpected behavior, and a hard problem to solve. You'd have to track current/after caret position. I can't remember if that can be done in javascript.

Peter Bengtsson

What about something like onkeyup? I like the idea of changing it as you type because then it's easier to see a long list of strings you've typed in so far.

Peter Bengtsson

Surely it must be a bug in Firefox Android that the caret isn't at the end even if the input value is changed in async events.

Peter Bengtsson

At least it works on iOS Safari. I just tested that.

Steve Friedl

This puts sites on my "No Dashes Or Spaces" Hall of Shame at unixwiz.net

Peter Bengtsson

That's funny! http://unixwiz.net/ndos-shame.html

I hate when sites can't process the numbers just because the visual presentation is different. For a server to do `.replace(' ', '')` before talking to the gateway is just so incredible trivial.

Neil Rashbrook

return value.replace(/\D/g, '').match(/.{1,4}|^$/g).join(' ');

This was the shortest code that I was able to write. I wasn't sure whether \D is an exact alias for [^0-9] but it says so on MDN (you could add a + to improve performance slightly). The |^$ is just one way to work around the otherwise null match for an empty value.

Man Hoang

Please check out my demo, which correctly handles the caret. You're gonna love it!

Peter Bengtsson

It works on Safari on iOS.

Mohamed shakir

it's working fine..

Dick from the internet

Shouldn't the length be max of 20 for non standard cards?


Can I use it my projects? What license?


How does it work with different card type? ie AE have different amount of numbers

Peter Bengtsson

It doesn't :)


hii i want to print account no like in this format 11-11-111-111
 how do i do ?


Nicholas Koskowski

Anyone have a patch for this to work with AMEX?

The Anything

Guys u just change " var matches = v.match(/\d{4,16}/g); " to " var matches = v.match(/\d{4,20}/g);" and it will work with all cards ;)


Here is a better and simpler way.

formatCreditCardNumber(unformattedNumber) {
        let pureNumber = unformattedNumber.split(" ").join("");
        let pureNumberArray = pureNumber.split("");
        let formattedNumberArray = [];
        for(let i = 0; i < pureNumberArray.length; i++){
            if (i != 0 && i % 4 === 0){
                formattedNumberArray.push(" ");
        return: formattedNumberArray.join("");


ONLY_DIGITS = "^[0-9]*$";

Nadeem Ijaz

here is a problem with while i am using this code it is working till 4 digits but after fourth digit when it add space then backspace all the code
please give me solution

Nadeem Ijaz

sorry its my mistake
i was using input type number that's why it removes spaces

Mike Skos

Thank you!!

Murat Polater

Here is a compact solution with jquery:

$("#card_number").keyup(function() {
var foo = $(this).val().replace(/\s+/g, "").replace(/[^0-9]/gi, ""); // remove non numeric
foo = foo.split(" ").join(""); // remove spaces
if (foo.length > 0) {
foo = foo.match(new RegExp(".{1,4}", "g")).join(" ").slice(-23); // max 23 chars


thank you

Your email will never ever be published.

Related posts

Go to top of the page