Question

Readonly Select Alternatives in HTML Forms

htmljavascript

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.

  • readonly applies 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.
  • disabled applies 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:

  1. Show a disabled <select> so the current choice is visible but cannot be changed.
  2. 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 readonly text field is sealed so the customer cannot alter it, but it stays on the list.
  • A disabled control 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.

Take Quiz

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:

  1. The browser examines form controls that have a name and are eligible for submission.
  2. The <select> is disabled, so it is skipped, even if it had a name.
  3. The hidden input is enabled and has name="role".
  4. The hidden input contributes role=member to the request body.
  5. The server receives role with the value member.

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.

Take Quiz

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

ApproachUser can change it?Submitted normally?Best use
Normal <select>YesYesThe user should choose a value.
<select disabled>NoNoDisplay a locked selection when no value needs to be submitted.
Disabled <select> + hidden inputNoYes, through hidden inputDisplay a locked selection and submit its value.
Text/<output> + hidden inputNoYes, through hidden inputThe value is informational; a dropdown appearance is unnecessary.
JavaScript event blocking on a normal selectUsually noYesRarely recommended; behavior and accessibility are easy to get wrong.

readonly is different from disabled:

AttributeTypical controlsEditable?Included in submission?
readonlyText-like <input> elements and <textarea>NoYes
disabledMost form controls, including <select>NoNo
Take Quiz

Cheat Sheet

  • <select> has no native readonly behavior.
  • disabled prevents interaction with a select.
  • Disabled controls are not included in GET or POST form 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 name to 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.

Take Quiz

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

  1. Display a select element showing the Business plan as selected.
  2. Prevent the user from changing the visible select.
  3. Submit the value business under the field name plan.
  4. Include a name field that the user can edit.
  5. Show the submitted values on the page instead of sending them to a server.
Take Quiz

Keep learning

Related questions

View more