Question
The HTML specification does not define a readonly attribute for <select> elements; it only supports disabled. A disabled form control prevents users from changing the selected option, but its value is not included in submitted POST or GET form data.
What is the best way to make a <select> appear read-only while still submitting its selected value with the form?
Short Answer
You will learn why <select> cannot be made read-only with HTML alone, how disabled affects form submission, and the reliable pattern of pairing a disabled <select> with a hidden input that submits the required value.
Concept
readonly and disabled solve different problems in HTML forms.
readonlyapplies to certain text-entry controls, such as<input type="text">and<textarea>. The user can focus and copy the value, but cannot edit it. Read-only controls are submitted with the form.disabledapplies to controls including<select>. The user cannot interact with the control, and the control is excluded from form submission.
A <select> does not support readonly. Writing readonly on it does not give it read-only behavior:
<select name="plan" readonly>
<option value="basic">Basic</option>
</select>
The browser may still allow the user to change the selection because readonly is not a valid behavioral attribute for <select>.
The usual solution is:
- Show a disabled
<select>so the current choice is visible but cannot be changed. - Submit a separate
<input type="hidden">with the same value.
This works because hidden inputs are successful form controls and are included in normal form submission. However, hidden inputs are not a security boundary: a user can alter any browser-sent form value. The server must validate that the submitted value is allowed for the current user and request.
Mental Model
Think of form submission as a shipping list.
- A normal form control is an item placed on the shipping list.
- A
readonlytext field is sealed so the customer cannot alter it, but it stays on the list. - A
disabledcontrol is marked “do not ship,” so it is left off the list entirely.
A disabled <select> lets people see the choice, but it is not shipped. A hidden input is the separate shipping label that carries the selected value to the server.
Syntax and Examples
A disabled <select> is displayed but not submitted:
<form action="/orders" method="post">
<label for="shipping-method">Shipping method</label>
<select id="shipping-method" name="shippingMethod" disabled>
<option value="standard" selected>Standard shipping</option>
<option value="express">Express shipping</option>
</select>
<button type="submit">Place order</button>
</form>
When this form is submitted, shippingMethod is absent because the <select> is disabled.
Use a hidden input to submit the value:
<form action="/orders" method="post">
<label for="shipping-method">Shipping method</label>
<select id="shipping-method" disabled>
<option value="standard" selected>Standard shipping</option>
<option value="express">Express shipping</option>
</select>
<input type="hidden" name="shippingMethod" value="standard">
<button type="submit">Place order</button>
</form>
The visible select is disabled, while the hidden input sends this data:
shippingMethod=standard
Keep the hidden input and displayed selection synchronized. If the selection is fixed when the page is rendered, set both values from the same server-side value. If JavaScript can change the select, update the hidden input too.
Step by Step Execution
Consider this form:
<form action="/profile" method="post">
<select id="role-display" disabled>
<option value="member" selected>Member</option>
<option value="admin">Administrator</option>
</select>
<input type="hidden" name="role" value="member">
<button type="submit">Save</button>
</form>
When the user submits it:
- The browser examines form controls that have a
nameand are eligible for submission. - The
<select>is disabled, so it is skipped, even if it had aname. - The hidden input is enabled and has
name="role". - The hidden input contributes
role=memberto the request body. - The server receives
rolewith the valuemember.
The selected text, Member, is for the user. The hidden field's value, member, is the stable machine-readable value sent to the server.
Real World Use Cases
This pattern is useful when an application needs to display a fixed choice while preserving it during submission:
- Checkout flows: Show the shipping country or fulfillment method determined earlier in the order process.
- Account forms: Display a role, organization, or account type that ordinary users may view but not edit.
- Multi-step forms: Preserve choices made in a previous step while showing them on a confirmation step.
- Administrative tools: Show a value that is locked by workflow rules, such as an approved department or invoice currency.
- Server-rendered forms: Render an existing database value as disabled and include its identifier in a hidden field for an update request.
In every case, the server should derive or verify sensitive values rather than trusting that a hidden field was unchanged.
Real Codebase Usage
In production code, developers typically use one of these patterns.
Render the display and submitted value from one source
On a server-rendered page, use the same saved value for the selected option and the hidden input. This prevents accidental mismatch.
<select id="country-display" disabled>
<option value="US" selected>United States</option>
</select>
<input type="hidden" name="country" value="US">
Prefer server-side authority for protected fields
For values such as user ID, account role, price, discount, or permissions, do not rely on a hidden field. The server should obtain the authoritative value from the authenticated session, database record, or server-side workflow state.
Synchronize only when changes are allowed by code
If a script changes the visible select, update its hidden counterpart:
<select id="status-display" disabled>
<option value="pending" selected>Pending</option>
<option value="approved">Approved</option>
</select>
<input id="status-value" type="hidden" name="status" value="pending">
<script>
const display = document.querySelector('#status-display');
const hidden = document.querySelector('#status-value');
function setStatus(value) {
display.value = value;
hidden.value = value;
}
setStatus('approved');
</script>
Use a read-only display instead of a select when appropriate
If users never need a dropdown-shaped control, plain text is often clearer and more accessible:
<p>Account type: <strong>Business</strong></p>
<input type="hidden" name="accountType" value="business">
This avoids presenting a control that looks interactive but is intentionally locked.
Common Mistakes
Assuming readonly works on <select>
<!-- Does not make the select read-only -->
<select name="country" readonly>
<option value="US">United States</option>
</select>
readonly does not apply to <select>. Use a disabled select plus a hidden input, or use plain text.
Expecting a disabled control to submit
<!-- country will not be sent -->
<select name="country" disabled>
<option value="US" selected>United States</option>
</select>
Disabled controls are excluded from submission by design.
Forgetting the hidden input's name
<!-- No name means no submitted key -->
<input type="hidden" value="US">
Use both a name and a value:
<input type="hidden" name="country" value="US">
Letting the visible and hidden values disagree
<select disabled>
<option value="US" selected>United States</option>
</select>
<input type="hidden" name="country" value="CA">
The server receives CA, not the visibly selected US. Populate both from the same value and synchronize them after programmatic changes.
Treating hidden fields as trustworthy
Users can modify hidden fields with browser developer tools or custom requests. Always validate submitted values and enforce authorization on the server.
Using CSS alone to block interaction
For example, pointer-events: none may stop mouse clicks, but it does not reliably prevent keyboard interaction and does not provide correct disabled semantics. It is not a replacement for proper form behavior.
Comparisons
| Approach | User can change it? | Submitted normally? | Best use |
|---|---|---|---|
Normal <select> | Yes | Yes | The user should choose a value. |
<select disabled> | No | No | Display a locked selection when no value needs to be submitted. |
Disabled <select> + hidden input | No | Yes, through hidden input | Display a locked selection and submit its value. |
Text/<output> + hidden input | No | Yes, through hidden input | The value is informational; a dropdown appearance is unnecessary. |
| JavaScript event blocking on a normal select | Usually no | Yes | Rarely recommended; behavior and accessibility are easy to get wrong. |
readonly is different from disabled:
| Attribute | Typical controls | Editable? | Included in submission? |
|---|---|---|---|
readonly | Text-like <input> elements and <textarea> | No | Yes |
disabled | Most form controls, including <select> | No | No |
Cheat Sheet
<select>has no nativereadonlybehavior.disabledprevents interaction with a select.- Disabled controls are not included in
GETorPOSTform data. - To show a locked select and submit its value, use:
<select disabled>
<option value="gold" selected>Gold</option>
</select>
<input type="hidden" name="plan" value="gold">
- A hidden input must have a
nameto be submitted. - Ensure the hidden value matches the displayed option.
- For sensitive values, validate on the server; hidden fields can be changed by clients.
- Prefer plain text plus a hidden input if a disabled dropdown would be misleading.
FAQ
Can I add readonly to a <select> element?
You can write the attribute, but it has no native read-only effect on a <select>. The control can still be changed unless another mechanism prevents it.
Why is a disabled select not sent in a form submission?
HTML excludes disabled controls from the set of successful form controls. This applies to both GET query strings and POST request bodies.
What is the standard way to submit a disabled select value?
Use a hidden input with the required name and value, while using the disabled select only for display.
Should the disabled select also have a name attribute?
It is not necessary because disabled controls are skipped during submission. Many developers omit the name from the display select and put the submitted name only on the hidden input.
Is a hidden input secure?
No. A hidden input is hidden visually, not protected. A client can alter it. Validate the value and authorization on the server.
Can CSS make a select read-only?
CSS can change appearance and may block pointer interaction, but it does not create reliable read-only form semantics. Use disabled for a locked select.
Is plain text better than a disabled dropdown?
Often, yes. If the user cannot change the value, plain text can communicate that more clearly. Use a hidden input only if the value must be submitted.
Mini Project
Description
Build a confirmation form for a subscription plan. The plan was chosen in an earlier step, so users should be able to see it but not change it on this page. The selected plan still needs to reach the server when the confirmation form is submitted.
Goal
Display a non-editable selected plan and submit its value as plan with the form.
Requirements
- Display a select element showing the Business plan as selected.
- Prevent the user from changing the visible select.
- Submit the value
businessunder the field nameplan. - Include a name field that the user can edit.
- Show the submitted values on the page instead of sending them to a server.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.