HTML Guides for readonly
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
A field with the readonly attribute is already exposed as read-only, so aria-readonly repeats the same information and can be removed.
The readonly attribute on an <input> or <textarea> tells browsers and assistive technologies that the user can't change the value. Adding aria-readonly="true" states the same thing a second time. The ARIA in HTML specification advises against it, and the validator reports it as a warning.
The same warning appears when aria-readonly repeats the exact value of readonly, as in readonly="readonly" aria-readonly="readonly", or an empty aria-readonly="" next to a bare readonly. In those cases the validator also reports a Bad value error, because aria-readonly only accepts true or false.
Remove aria-readonly and keep readonly. aria-readonly is meant for custom widgets, such as an element with role="textbox" or a grid cell, which have no native readonly attribute. If aria-readonly is set to false on a field with readonly, the two attributes contradict each other, and the validator reports that as an error instead.
Invalid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly aria-readonly="true">
Valid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly>
A field with the readonly attribute can't also have aria-readonly="false": remove aria-readonly and let the native attribute set the state.
The readonly attribute on an <input> or <textarea> already tells browsers and assistive technologies that the user can't change the value. aria-readonly="false" claims the opposite, so the field exposes two conflicting states. The ARIA in HTML specification forbids aria-readonly="false" on any element that has readonly, and the validator reports it as an error.
This often comes from a component or template that always outputs aria-readonly from a flag that defaults to false, while readonly is added separately. Decide what the field should be. If users shouldn't edit it, keep readonly and drop aria-readonly. If they should, remove readonly. aria-readonly is meant for custom widgets, such as an element with role="textbox" or a grid cell, which have no native readonly attribute.
Writing aria-readonly="true" next to readonly doesn't trigger this error. That combination is only redundant, and the validator reports it as a warning.
Invalid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly aria-readonly="false">
Valid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly>
A field with the readonly attribute has an aria-readonly value that doesn't match it: remove aria-readonly and keep the native attribute.
aria-readonly only accepts true or false. A value such as TRUE, 1, undefined or readonly is neither, so the validator reports two errors on the same field: a Bad value error for aria-readonly, and this one, because the value differs from what readonly states. Values like these usually come from a template that prints a variable straight into the attribute, for example a boolean that the template language writes as 1 or True.
The readonly attribute already exposes the read-only state to assistive technologies, so the field doesn't need aria-readonly at all. Remove it. If you're building a custom widget that has no native readonly attribute, such as an element with role="textbox", write aria-readonly as exactly true or false.
Other values give different messages. aria-readonly="false" next to readonly is its own error, because it contradicts the native attribute. aria-readonly="true", or a value that exactly repeats the value of readonly (such as readonly="readonly" aria-readonly="readonly"), only gets a warning that the attribute is unnecessary.
Invalid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly aria-readonly="1">
Valid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly>
The HTML specification restricts the readonly attribute to input types where the user would normally type or select a textual/numeric/date value. The idea is straightforward: readonly means "you can see and select this value, but you can't edit it." That concept only makes sense for fields that contain editable text or structured data like dates and numbers. For controls like checkboxes, radio buttons, color pickers, file selectors, and range sliders, the interaction model is fundamentally different — there's no text to make "read-only."
The full list of input types that support readonly is:
textsearchurltelemailpassworddatemonthweektimedatetime-localnumber
The readonly attribute is not valid on these types: checkbox, radio, range, color, file, hidden, button, submit, reset, and image.
Why this matters
Standards compliance: Browsers are not required to honor readonly on unsupported input types. Even if a browser appears to respect it today, that behavior is not guaranteed and could change.
Form submission behavior: There's a critical difference between readonly and disabled. A readonly field's value is included in form submission data, while a disabled field's value is not. If you swap readonly for disabled to fix this error, be aware that the field's value won't be sent with the form unless you take additional steps (such as adding a hidden input).
Accessibility: Screen readers and assistive technologies rely on valid HTML to convey the correct state of form controls. Using readonly on an unsupported type can send confusing signals about the control's interactivity.
How to fix it
You have several options depending on your goal:
- Remove
readonlyif it was added by mistake or isn't necessary. - Change the input type to one that supports
readonly, if that fits your use case. - Use
disabledinstead to prevent interaction on non-textual inputs. Remember that disabled fields are excluded from form submission. - Pair
disabledwith a hidden input if you need the value submitted with the form but want the visible control to be non-interactive.
Examples
Invalid: readonly on a checkbox
<input type="checkbox" name="agree" readonly>
Invalid: readonly on a range input
<input type="range" name="volume" min="0" max="100" value="50" readonly>
Invalid: readonly on a color input
<input type="color" name="theme" value="#ff0000" readonly>
Fixed: using readonly on a supported input type
<input type="text" name="code" value="ABC-123" readonly>
<input type="email" name="contact" value="user@example.com" readonly>
<input type="date" name="start" value="2024-01-15" readonly>
Fixed: using disabled for a non-textual input
<input type="checkbox" name="agree" checked disabled>
Fixed: using disabled with a hidden input to preserve form submission
If you need the value to be submitted with the form while keeping the visible control non-interactive, pair a disabled control with a hidden input:
<!-- The hidden input ensures the value is submitted -->
<input type="hidden" name="agree" value="on">
<!-- The disabled checkbox is visible but non-interactive -->
<input type="checkbox" name="agree_display" checked disabled>
<label for="agree_display">I agree to the terms</label>
Fixed: using JavaScript to prevent changes (advanced)
If you truly need a checkbox that looks interactive but can't be changed, you can use JavaScript while keeping the HTML valid:
<input type="checkbox" name="agree" checked onclick="return false;">
Note that this approach relies on JavaScript and won't work if scripting is disabled. For most cases, using disabled (with or without a companion hidden input) is the simpler and more robust solution.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries