Why the number input is the worst input
[Ed. note: While we take some time to rest up over the holidays and prepare for next year, we are re-publishing our top ten posts for the year. Please enjoy our favorite work this year and we’ll see you in 2023.]
I’ve been writing code for at least 15 years. While I’ve used a lot of technologies and built a lot of stuff over the years, the thing I have done the most is the front end, i.e. HTML, CSS, and JavaScript. And of all the things I’ve built on the front end, the thing that I’ve built most often is forms.
I won’t pretend to be the expert on all things related to forms. However I will say this: of all the difficult, unconventional, and unusual requirements I’ve been asked to deliver over the past 15 years, most of those tasks revolved around building forms. Each one presented a challenge that required some sort of outside-the-box or non-standard solution. I’ve learned a lot of ways to overcome those challenges: what you should do, and more importantly, what you should not do when trying to meet those challenges.
Of all the things that I’ve learned about what not to do, one of the easiest and best pieces of advice I can share is this: unless the use case is drop-dead simple, you should avoid using the number input.
I’ve seen so many forms that I felt compelled to build my own form builder that could handle at least some of the more challenging requests without the hassle of writing code from scratch every time. Keenforms is a drag-and-drop form builder with a no-code rules engine. It makes it easier to create dynamic and conditional functionality without having to write code from scratch.
Recently I got some feedback about Keenforms, and the criticism I heard the most was the fact that Keenforms does not use the <input type="number" />
.* We do have an input that’s labeled as a number, but when you add it to your form, it actually renders as <input type="text" />
.
*Please note that Keenforms actually does use the number input inside our app. However, we only use it in the dashboard and for only the simplest inputs.
The pro-number input crowd has legitimate concerns, primarily around accessibility. A number input has native increment/decrement buttons. It has built-in validation to verify it is a number. Certain mobile devices will show a number keypad instead of the full keyboard, making it easier to enter data. Using the number input makes it easier for screen readers as well.
There were also some valid complaints about developers overusing JavaScript (guilty!). I understand some people’s aversion to JavaScript. However those aforementioned forms that I’ve built with those challenging requirements all required heavy use of JavaScript. It’s the only way to provide instant real-time feedback when creating dynamic conditional logic. Keenforms relies heavily on JavaScript and specifically React.
There are so many drawbacks related to number inputs when dealing with complex and conditional logic, much of it related to JavaScript issues, that for the past few years I’ve decided to avoid using it.
It should be noted I am not alone in the anti-number input camp. The UK Government posted an article detailing some of the problems related to the number input.
The thing is, the issues cited in that article weren’t even the primary reasons why I decided to avoid it. After receiving that feedback on Keenforms, it occurred to me that there are quite a few programmers out there who are completely unaware of the problems the number input presents. The list isn’t long, but the issues are rather jaw-dropping. So here’s a short list of all the horrible things about the number input (at least the ones that I know about) that every developer should know:
- When the number input contains an invalid value and you retrieve the value, you get a blank string.
- Valid numbers include more than just digits (i.e,. scientific notation like the letter e).
- The min/max attributes can easily be bypassed.
- Different browsers accept different characters.
When the number input contains an invalid value and you retrieve the value, you get a blank string
There are a couple of ways you might go about retrieving the value. It could be on an event listener, which would store the field value in event.target.value
. Or you could get it through the DOM element. Either way when the number is invalid you can’t get the actual value that appears on screen in the <input type="number">
field:
const numberInput = document.getElementById('id_here');
console.log(numberInput.value); // will return empty string if invalid
If you are building a form that requires conditional validations or calculations, it is hard to understate just how big of a problem this is. The fact that the number input will allow a user to enter an invalid number value but not actually retrieve that invalid value (getting a blank string instead), makes the kind of validation I routinely get asked to deliver impossible.
If you are just using a number input for someone’s age or the quantity of a product, this might not seem like that big of a problem. However, consider an example of a form I was tasked with building where the number input either affects or is affected by other inputs, such as a life insurance form with the following requirements:
- A number input for the quantity of dependents. The number value will determine the quantity of nested forms for each dependent.
- A number input for each household member that indicates the percentage this person would receive in the event of a payout. If the quantity of dependents is one, then this particular input would be hidden and automatically set to 100. If the quantity is greater than 1, then the input should be visible, and have a value of no less than 0.01, and contain a value less than 100.
- A hidden number input that would sum total the percentage of each dependent listed, validating that the subtotal is exactly 100.
All of these requirements necessitate using JavaScript. Generally, those might be the minimum requirements, and additional requests usually come in after some user testing. I can’t do my job without being able to access those values, whether they are valid or not.
A highly usable form will leverage JavaScript to prevent users from entering or submitting invalid data. Back-end data validation is not just enough any more. Most of us find it annoying when we go through the trouble of entering data into a form without issue, only to click submit and find out some input was invalid. Using JavaScript for this kind of validation is absolutely essential for the kind of quality user experience most of us have come to expect.
Valid numbers are more than digits
This is the flipside of the invalid number value issue: the number input allows you to enter values that are technically acceptable numbers, but there is a good chance you don’t want them and didn’t anticipate that they could be considered “valid.”
Chances are likely that you used the input type="number
” because you expected an integer to represent age or quantity. However, the number input will allow for scientific notation values; for example, 2.3e4, which represents 2.3 times 10 to the power of 4, or 23,000. If the number input value grows large enough, some browsers will automatically convert your input into exponential notation.
Different browsers accept different characters
All browsers are not the same. Let’s start with the best case scenarios.
Chrome and (surprise!) Microsoft Edge
The following characters are permitted (based on my hastily-done first-hand testing);
- Numbers 0-9
- Decimal point
- “-” (for negative values)
- characters “+” and “e” for exponential notation

Both of these browsers will prevent you from entering the accepted non-numeric characters more than once. However, you can place those symbols anywhere in the input, like putting the minus symbol in between digits, which would make the number invalid and therefore making that value inaccessible via JavaScript.
Firefox and Safari
There are no limits whatsoever. You can type whatever you want.
All of these browsers will show a built-in popup to indicate that the value you’ve entered is not a valid number, and the Submit button will not work without the user fixing those input values. The built-in validation is visually inconsistent to whatever UX when you are building for your app. As a programmer, you might find this acceptable, but there’s a good chance your designer and/or product manager will not.

Min/max limits can be bypassed
The ability to set the minimum and maximum number values in the number input is a nice feature to have. The increment/decrement buttons will keep the number value within these range parameters. However, you cannot completely trust it to prevent out of range values; it is possible to copy/paste a value beyond those limits.
<input type="number" min="1" max="10" />
If this seems petty, I want you to know I totally agree with you. But it’s not always my call.
Imagine a software tester finds this issue and logs a bug. Imagine the product manager hears about the bug. Imagine discussing this bug during sprint planning, but then pleading your case to that same product manager: “This is an edge case and the number will still be validated on the back end.” And besides, you said this was MVP and this “bug” is actually standard behavior of the number input. Imagine losing that battle and having to fix it anyway.
The bottom line is that it’s not always the developer’s choice, and the number input is not your friend.
What to use instead of number inputs
Before I go into detail about when to use the number input, it’s important to establish something that many experienced programmers know, but which is worth repeating; You should only use the number input when dealing with mathematically relevant numeric values, i.e. the quantity of a product or someone’s age. That means you never use the number input for things like ZIP codes, phone numbers, social security numbers, etc.
On to the more interesting stuff. Whether to use the number input must be decided on a case-by-case basis, and the calculus will change based on requirements, risks, and stakeholders.
If you need a number input that has no conditional validation (i.e. number is always within a certain range of min/max) and has no relationship with any other inputs on the form, then using the number input is not a big deal. As mentioned, Keenforms does use the number input for the position value of a form attribute. The risks and consequences of a user wanting to do something malicious (like putting invalid number on the position value) to their own form is low enough that it’s a worthwhile tradeoff.
Make sure the stakeholders are aware of this issue of visual inconsistencies across different browsers as well as inconsistencies of whatever messaging you are using to indicate invalid number input values.
If you are dealing with conditional validation or that number is being used elsewhere for a calculation, that’s when you need to ditch the number input. There are a couple of options on how to deal with this issue:
The Gov.UK article mentions a possible solution: Using <input type="text" inputmode="numeric" pattern="[0-9]*">
is a nice option for integers, but it won’t work for floating point decimal numbers.
Your last option is to do what I built for Keenforms: use a simple input type="text
“, which will always give you its value, and do all your validation via JavaScript. It requires more effort on the part of the programmer and you lose the increment/decrement buttons. As someone who has learned the hard way, though, it’s a tradeoff that I’ve had to accept. For advanced use cases I have not found another viable option.
That is not to advocate for sacrificing all accessibility for the sake of advanced requirements, visual consistency, and ease of use. I recently spoke with Avram Sand from Access Armada to discuss some of the problems related to the number input.
“The purpose of digital accessibility is to ensure that your forms are understandable and usable for everyone,” said Avram. “Number input fields can help, but there are other ways to get to our desired outcomes, for example, by forcing a numeric keypad on a text input, adding descriptive field labels that indicate the requirements to enter a number, and form validation and errors that work seamlessly with screen readers and other assistive technologies.”
My best recommendation for trying to maintain as much as accessibility as possible is to use an online tool that will check your webpages for accessibility, such as the WAVE accessibility tool.
If there’s one takeaway here, it’s that it’s impossible to deliver the kinds of complex functionality many projects demand while simultaneously using the number input. It’s an either/or choice, and the way I see it, you’re better off ditching the number input.
Tags: front-end, input type, javascript, web forms
42 Comments
Next: why the “date” input is even worse! 🙂
I can relate to this issue of dealing with number input and HTML forms in general. There are too many nuances and edge cases that need to be considered. At Formester, we believe accessibility is the most important thing and should not be sacrificed unless it is absolutely required.
Our number type fields are always number by default, and we try to add validations around it using Javascript/Vue to guide users, but we try to stick to the native types. So much so that we even insist on using the default select/radio/checkbox that native HTML provided course with normalization and our own styling.
If entering scientific notation, a “-” means a negative exponent. E.g., “1e-3” should be “0.001” just as “1e3” means “1000”.
agreed
But you can fix these issues instead of use something totally different
For example for the empty string I always mark the input invalid, and in one project I was asked to reset the value to empty, mark it invalid when the user write invalid number
For the min and max I double check on my js code
So you for sure can overcome all of this and give native support for everyone
Sure you can reinvent the wheel: write your own client-side validation (which, I regret to inform you, is no more dependable than the built-in validation), write your own input restrictions, force-specify mobile numeric keyboard, and rewrite all of the accessibility features built into the browser just to eliminate a minor nuisance, but is it really worth it? Are you sure that you know better than Google and Mozilla and Apple how to make sure screen readers know that your input is for numbers only?
More important than visual consistency within your website is visual consistency with every other website in the world. Most people only use one browser. When every website’s validation warnings look the same, that’s consistency. When your website looks different, that’s confusing.
The examples you give of why you need to process numbers client-side to change the form on the fly are all examples of bad form design. You should not ask “how many dependents?” and then use that to plop more form fields onto the page willy-nilly. You should have a button for “add dependent” that adds a new context for each dependent. Let the fact that there are four dependents added indicate that there are four dependents. Arrange them into a nice table.
As for calculating client-side that values add up to 100%, the only reason to do that is to provide user feedback that the form values are invalid. Guess what? If you have invalid number fields, your form values are invalid. No need to add the values to see if they add up to 100% — there’s a problem either way. And like I said above, your client-side validation is useless in the long-run, because it _will not stop bad actors._ Ever.
And regarding the “arbitrary and capricious” tester, as a software tester myself, I’ll give you a much harder time about your roll-your-own-code adventures because as soon as I see you’ve used a text field for a number, I’m thinking “uh-oh, the developer thought they were being clever,” and let me tell you, bugs run rampant when developers think their clever. You’d better believe I’m going over that field with a fine-tooth comb, with the screen reader, with the accessibility tools, the security tools, prodding your client-side validation this way and that to find bugs. If I see a number input, I’m like “cool, it’s a number input.” Google and Mozilla know what they’re doing and I’m not testing their code anyways.
Finally, while some PM’s will demand that every little thing look exactly how they envisioned it, pixel-perfect, most PM’s will run you up the river for spending two days on something that could have been done in two minutes with much less risk.
Some good points. But client-side validation is useful to non-bad ‘actors’ (why do we call them that anyway?), which are most of the users. It is like saying that traffic lights are useless because sometimes people run them. (When all cars are self-driving, there will be no need for traffic lights, right? And probably no need for traffic cops either.)
“Why do we call them that?”
Well, “bad actor” as a general term dates back to 1901, well before software and web development, so there’s that. But even if you weren’t getting the term “actor” from that, using it as a term for users and external systems in interactions comes from Use Case-based design in the OOP world and was an early diagram type in UML, so even that predates HTML and modern web design.
Great points! Good to see multiple perspectives in here 👍
I think the validation has much less to do with bad actors and more to do with instant UX feedback. The writer mentioned this when he talked about filling in a form, the backend responding with some errors and then having to fix them versus instantly seeing them client side.
When I build forms and add client side validation, it’s not to prevent bad actors from doing anything, as bad actors can bypass any client side code, but it’s primarily for the UX
Thank you, and very nicely put.
> And regarding the “arbitrary and capricious” tester, as a software tester myself, I’ll give you a much harder time about your roll-your-own-code
> adventures because as soon as I see you’ve used a text field for a number, I’m thinking “uh-oh, the developer thought they were being clever,” and let
> me tell you, bugs run rampant when developers think their clever. You’d better believe I’m going over that field with a fine-tooth comb, with the screen
> reader, with the accessibility tools, the security tools, prodding your client-side validation this way and that to find bugs.
Good to see you’re taking your job seriously. Nicely written piece overall.
You say to use input type=text, and do all your validation in JavaScript. Why not just use type=number, and do all your validation in JavaScript. How is validation any different? The user can still input anything in text, so I don’t see how you are saving yourself anything. You mention an invalid number will result in an empty string. Why can’t you include that in your JavaScript validation?
Number types have issues with leading zeros. I live in a place with two leading zeros in the zip code and my phone number has a leading zero in the “area code”.
Values with a leading zero are not numbers, they are strings/text. Just because something is made up of digits does not mean it is a number. A quick visual example of this is trying entering “0123” in a spreadsheet and see what happens – you get 123. If you want to have that leading zero retained you must force it to a string; usually prefixing it with a single quote character.
Yes, my hatred of leading zeroes is extreme, ha ha. Just stick a 1 on the front and move on. If you need that extra number space, get a bigger computer. Gosh.
I recently had a similar experience with type=”date”.
What’s your opinion on using date fields?
We should use Julian Dates instead 🙂
> The Gov.UK article mentions a possible solution: Using is a nice option for integers, but it won’t work for floating point decimal numbers.
What about “? Regular expressions are quite versatile and allow a more declarative method of specifying input formats than JavaScript.
You say the solution from the Gov.uk article doesn’t support floating point numbers. What about pattern=”[0-9]*(\.[0-9]+)?|[0-9]+\.[0-9]*”? Regular expressions are quite versatile and allow a more declarative method of specifying input formats than JavaScript.
Or just use inputmode decimal (see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode).
Disagreed. I love the built-in input types. For any edge-case, specific needs we have custom elements.
TL;DR
There is a difference between an entity that is a number and an entity composed of numbers. Number is meant to contain the former.
As pedantic as that sounds, it’s actually par for the course. For example mathematics makes a distinction between ordinal, cardinal and nominal numbers.
Yu wrote: *”some sort of out-of-the-box or non-standard solution”* – I’m pretty sure the expression is *outside the box* – which means the opposit of “out-of-the-box”.
Never in 20 years have I had any of the 4 mentioned issues with type number input.
This is typical overthinking at its worst.
They are of no consequence, all common users can’t do what is mentioned here, it is not to their knowledge or intent to do anything other than enter a number in a number input and device keyboard as mentioned presents numerical keyboard on devices and prevents regular keyboard input, and special chars are very difficult to access, making it difficult or extremely unusual for somone to have been able to input any type of other special numerical value beyond the standard set.
Anyone trying to do anything beyond the intended input is a malicious user who should not achieve anything because backend should never trust, always validate & sanitise all user input.
The one actual minor, bearly worth mentioning, annoying thing here not mentioned in the article; (even though you should always cast to Number) the type input number returns a value type of string not number when you access the field value using JavaScript.
That is the only tiny thing that bothers me the most about Number input fields, I love them otherwise.
Just saying.
I like the details in this blog post!
Learned a few things I didn’t know about the number input.
I do agree with some of the comments here though, handling the bad parts of the number input sounds way easier(and better) than creating your own solution.
Date field also has some weird behavirours across the browser..
You can use `numberInput.valueAsNumber` to access a numeric value. (It returns `NaN` for illegal values.) It also avoids the coercion foot-gun which means this is true: `numberInput.value = 4; console.assert( numberInput.value + 4 === “44”)` It only works for numeric types.
`pattern` is a regex, so you can extends it to floats; e.g. `pattern=”[0-9]+(\.[0-9]*)?”` allows decimals (while requiring a leading digit).
(And, yes, date is definitely beyond rescue.)
Maybe there should be a standard format for dates? And a standard calendar? Standard letterset for writing, standard measurement system, standard… Oh, nevermind.
It must be inputmode digit, numeric and patterns also given here only. No need of keypress or pattern function.
This didn’t even get into how different cultures write numbers. E.g. a comma is a used a decimal place, and spaces are used to denote thousands.
Hideous. How did that EVER happen? Comma means it continues, period means the end, full stop. Don’t get me started on the big-endian little-endian thing, which should have been killed as soon as it was brought up the first time. Make decisions for the future. Analog TV standard stood for 70 years. AM standard still going strong after 100. Be an Engineer.
Deve ser dígito do modo de entrada, numérico e padrões também fornecidos aqui apenas. Não há necessidade de pressionamento de tecla ou função de padrão.
Por exemplo uma vírgula, é uma casa decimal usada em espaços para de tomar milhões .
Deve ser dígito do modo de entrada, numérico e padrões também fornecidos aqui apenas. Não há necessidade de pressionamento de tecla ou função de padrão.
I thought input type was just a hint about which type of keyboard a mobile browser should show. Making the universal number validator is impractical if not impossible.
Most lovely(sarcasm) thing about input[type=number] is how you can change its value by scrolling, if that field has focus. As well as arrow buttons, which in normal text fields just work as Home/End.
Many years ago when I was teaching ICT and Computer Science, I advised my students to use a simple rule, that it’s a number if you’re countng something – sheep, money, temperature. If it’s just numeric data, such as telephone numbers, flat numbers or similar, simply use text. Still seems to apply…
Bem, como um termo geral remonta em 1901, bem antes do desenvolvimento de software e web, então é isso. Mas mesmo que você não tenha obtido o termo disso, para usá-lo como um termo para usuários e sistemas externos em interações vem do design baseado em Caso de Uso no mundo OOP e foi um tipo de diagrama inicial em UML, portanto, mesmo que antecede o HTML e o web design moderno.
Publicação do stackoverflow .blog . Peça bem explicada .
> Using is a nice option for integers, but it won’t work for floating point decimal numbers
Why not just add a period `.` to the `pattern` for floats? Why doesn’t that work?
O que fazer com perguntas/ comentários
A sugestão de chat em comentários só surge quando ambos os usuários envolvidos tem reputação suficiente para falar em salas de bate-papo, o que não era o caso do autor da pergunta até aquele presente instante. Devido a isso, o moderador interviu e gerou a sala.
Não há muito o que se fazer nesse caso, pois o autor precisa ter no mínimo 20 de reputação e ele não tinha, então nem criando uma sala particular ele conseguiria falar, provavelmente.
Thank you, interesting topic!
Weird that straight after reading this today I discovered a bug in Excel’s COUNTIF function in an existing spreadsheet. Search string is 2e2, target range of strings includes it three times, COUNTIF returns 4 because target range also includes the string 200. Apparently (or so I read elsewhere on here after searching for “prior art”), “COUNTIF is designed to interpret data as numerical, where possible, irrespective of the datatype of the values in question.”
Jared Toporek, I’m not an HTML and JavaScript expert, but I’ve been a programmer for over 30 years, so I have to rely on my understanding and intuition that your article is 100% fair.
After spending over 4 hours today researching and testing solutions to a problem that should be absolutely simple, I can only declare happiness to find someone who agrees with me that “number input” is a terribly implemented invention.
I understand the good intentions of this, but if you just need a few details beyond the basics, it just becomes a waste of time and patience.
My need today is simple: I just want to paste (Ctrl-V) for example ” 36,123 ” and get “36123” in the “input” field, press Enter and get the form submitted.
Is it too much to ask?
In Chrome it works, but in Firefox (which is my default choice), it doesn’t.
OK! So let’s do some code to fix that.
But, surprise, “When the number input contains an invalid value and you retrieve the value, you get a blank string” (yes, a sub title of your article).
So… just tired of fighting such unglorious war, I’m going to use good old “text input” to make this simple magic happen.