Question
How can I hide the spin buttons that some browsers display for an HTML5 <input type="number"> element?
I want a consistent way, ideally using CSS or JavaScript, to prevent the up/down arrow controls from appearing in browsers such as Chrome.
<input id="test" type="number">
Short Answer
By the end of this page, you will understand how browser spin buttons work on <input type="number">, how to hide them with CSS in common browsers, what limitations exist, and when it may be better to use a different input type altogether.
Concept
<input type="number"> is an HTML form control designed for numeric input. Many browsers add built-in UI controls such as small up/down arrows, often called spin buttons or spinners, to let users increase or decrease the value.
These controls are part of the browser's native rendering of the input. That means:
- They are not plain HTML elements you can directly remove.
- Styling them is often browser-specific.
- There is no single universal CSS rule that works identically everywhere.
Why this matters:
- You may want a cleaner design without browser-provided arrows.
- Different browsers render number inputs differently.
- Form controls often require vendor-prefixed CSS for consistent appearance.
A common solution is:
- Use
appearance-related CSS where supported. - Target WebKit/Blink pseudo-elements for Chrome, Edge, and Safari.
- Adjust Mozilla appearance rules for Firefox.
Even after hiding the visible spin buttons, the input is still usually a number field. It may still:
- accept numeric validation,
- respond to arrow keys,
- respond to mouse wheel changes in some cases.
So hiding the arrows changes the appearance, not necessarily all number-input behavior.
Mental Model
Think of <input type="number"> like a standard numeric text box wrapped in a browser-made control panel.
The browser may attach tiny built-in buttons to the side. You cannot always remove the whole control panel in one standard way, but you can often tell each browser: render this more like a plain text field.
So the goal is not "delete these HTML elements" but rather "ask the browser to display this native control differently."
Syntax and Examples
The most common CSS approach is to target browser-specific appearance rules.
<input id="test" type="number">
/* Chrome, Safari, Edge, Opera */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
What this does
::-webkit-inner-spin-buttonand::-webkit-outer-spin-buttontarget the spinner controls in WebKit/Blink-based browsers.-webkit-appearance: none;hides those controls.margin: 0;helps remove extra spacing left behind in some browsers.-moz-appearance: textfield;makes Firefox render the number input more like a regular text field.
More modern version
Some projects also add the standard appearance property:
input[type="number"] {
appearance: textfield;
-moz-appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
Example with styling
<label for="age">Age</label>
<input id="age" type="number" min="0" max="120" placeholder="Enter age">
input[type="number"] {
appearance: textfield;
-moz-appearance: textfield;
padding: 8px;
font-size: 16px;
width: 200px;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
This keeps numeric validation while removing the visible arrows in many browsers.
Step by Step Execution
Consider this example:
<input id="qty" type="number" value="3">
input[type="number"] {
-moz-appearance: textfield;
appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
Here is what happens step by step:
- The browser reads the HTML and creates a number input.
- Because the input is
type="number", the browser may decide to render built-in spin buttons. - The CSS selector
input[type="number"]matches the field itself. appearance: textfieldand-moz-appearance: textfieldtell supporting browsers to display it more like a normal text box.- In WebKit/Blink browsers, the pseudo-elements
::-webkit-outer-spin-buttonand::-webkit-inner-spin-buttonmatch the spinner UI parts. -webkit-appearance: noneremoves their default visible styling.- The input still remains a number input, so browser validation and numeric semantics still apply.
If the user types abc, the browser may reject it or mark the field invalid depending on the browser and form behavior.
If the user presses the up arrow key, the value may still increase, because that behavior belongs to the number input itself, not just the visible spin buttons.
Real World Use Cases
Developers hide number-input spin buttons in cases like these:
- Custom design systems: the browser arrows do not match the app's visual style.
- Mobile-friendly forms: the team wants a cleaner field and relies on native numeric keyboards instead.
- Checkout forms: quantity, ZIP code, OTP, or pricing fields may need a simpler appearance.
- Admin dashboards: numeric filters and settings inputs may use custom increment/decrement buttons instead of native ones.
- Embedded widgets: compact UIs often remove default controls to save space.
Example scenarios:
- A shopping cart uses custom
+and-buttons around a quantity input. - A finance form accepts amounts but wants all fields to look visually identical.
- A settings page uses JavaScript validation and prefers a uniform style across browsers.
Real Codebase Usage
In real projects, developers usually do more than just hide the arrows.
Common patterns
- Design-system reset: remove browser-specific styling so all form fields start from a predictable baseline.
- Guarded validation: keep
type="number"for browser validation, but add custom checks for range, decimals, or required values. - Custom controls: hide native spinners and provide separate increment/decrement buttons.
- Input constraints: combine
min,max, andstepwith styling. - Event handling: prevent accidental wheel-based changes in fields like prices or quantities.
Example: custom quantity field
<div class="quantity-control">
<button type="button" id="decrease">-</button>
<input id="quantity" type="number" min="1" value="1">
<button type="button" id="increase">+</button>
</div>
input[type="number"] {
appearance: textfield;
-moz-appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
const input = document.getElementById('quantity');
const increase = document.getElementById('increase');
const decrease = document.getElementById('decrease');
increase.addEventListener('click', () => {
input.stepUp();
});
decrease.addEventListener('click', () => {
if (Number(input.value) > Number(input.min || 0)) {
input.stepDown();
}
});
This is common in real codebases because it preserves number semantics while using custom UI.
Common Mistakes
1. Expecting one universal CSS rule
A beginner might try:
input[type="number"] {
display: textfield;
}
This does not work because display is the wrong property.
Use browser appearance properties instead.
2. Forgetting browser-specific pseudo-elements
Broken example:
input[type="number"]::spin-button {
display: none;
}
There is no standard ::spin-button selector for this use case.
Use:
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
}
3. Hiding the arrows but forgetting behavior still exists
Even if the arrows are hidden:
- arrow keys may still change the value,
- the mouse wheel may still change the value,
- validation rules still apply.
If you need to stop wheel changes, you may add JavaScript:
const input = document.getElementById('test');
input.addEventListener('wheel', (event) => {
event.preventDefault();
}, { passive: false });
Use this carefully, because it affects usability.
4. Using type="text" without considering trade-offs
Some developers switch to:
<input type="text">
This removes number-field behavior, but you also lose built-in numeric validation and number-specific browser behavior.
5. Assuming all browsers behave exactly the same
Native form controls are one of the least uniform parts of the web platform. Always test in the browsers your users actually use.
Comparisons
| Approach | Keeps numeric semantics | Hides visible arrows | Cross-browser consistency | Notes |
|---|---|---|---|---|
type="number" with CSS spinner-hiding rules | Yes | Usually | Moderate | Most common solution |
type="number" with no custom CSS | Yes | No | Low | Browser decides appearance |
type="text" with custom validation | No built-in number semantics | Yes | High for appearance | More manual validation needed |
Custom UI with buttons + type="number" | Yes | Yes | Moderate | Good for quantity controls |
type="number" vs type="text"
| Feature | type="number" | type="text" |
|---|---|---|
| Built-in numeric validation | Yes | No |
| Native spin buttons | Often yes | No |
| Numeric keyboard on some devices | Often yes | Not guaranteed |
| Custom visual control | Harder | Easier |
| Accepts only numeric intent semantically | Yes | No |
If you only want to hide the arrows, keep type="number" and style it.
If you need full visual and behavioral control, type="text" plus validation may be more appropriate.
Cheat Sheet
<input type="number">
/* Firefox */
input[type="number"] {
appearance: textfield;
-moz-appearance: textfield;
}
/* Chrome, Safari, Edge, Opera */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
Key points
- Spin buttons are browser-native UI.
- There is no single fully universal CSS rule.
- WebKit/Blink browsers use
::-webkit-inner-spin-buttonand::-webkit-outer-spin-button. - Firefox often uses
-moz-appearance: textfield. - Hiding arrows does not remove number input behavior.
- If needed, use JavaScript to stop wheel-based changes.
- Test in real browsers before shipping.
FAQ
Can I completely remove number input spin buttons in every browser with one CSS rule?
No. Native form controls are browser-specific, so you usually need different CSS rules for different engines.
Does hiding the arrows change the input from a number field into a text field?
No. It still remains type="number" unless you change the HTML.
Why do the arrows disappear in one browser but not another?
Because browsers render native controls differently and support different appearance properties and pseudo-elements.
Should I use JavaScript to hide the spin buttons?
Usually no. CSS is the normal approach for hiding the visible controls. JavaScript is more useful for changing behavior, such as preventing wheel-based value changes.
Is type="text" better if I want full styling control?
Sometimes, yes. But you lose built-in numeric validation and number semantics, so you must replace that behavior yourself.
Can users still change the value with the keyboard after I hide the arrows?
Often yes. Hiding the visible buttons does not necessarily disable arrow-key behavior.
Do hidden spin buttons affect accessibility?
They can. If you remove native controls, make sure users still have a clear and usable way to enter or adjust numeric values.
Mini Project
Description
Build a small quantity input component for a shopping cart. The number field should keep numeric behavior, but the browser's native spin buttons should be hidden. Instead, users will use custom + and - buttons to change the value.
Goal
Create a styled number input with hidden native spinners and custom increment/decrement controls.
Requirements
- Create an HTML number input with a minimum value of 1.
- Hide the native spin buttons in common browsers using CSS.
- Add a minus button to decrease the value.
- Add a plus button to increase the value.
- Prevent the value from going below the minimum.
Keep learning
Related questions
Angular ngClass Conditional Class Binding Explained
Learn how to use Angular ngClass for conditional classes, fix common binding mistakes, and understand why this template error happens.
CSS :not() Selector for Elements Without a Class or Attribute
Learn how to use the CSS :not() selector to target elements that do not have a specific class or attribute, with examples and common pitfalls.
CSS Font Scaling Relative to Container Size: %, em, rem, vw, and Responsive Text
Learn how CSS font scaling really works and how to make text responsive using %, em, rem, vw, clamp(), and media queries.