aria-readonly
Indicates that the element is not editable, but is otherwise operable. Unlike disabled elements, readonly elements remain focusable and their values are submitted with forms.
Overview
The aria-readonly attribute indicates that an element is not editable but is otherwise operable. This is different from aria-disabled—readonly elements can still be focused and their text can be selected and copied.
The key distinction is that readonly elements are still interactive for viewing purposes. Users can tab to them, select text, and copy values. They simply cannot modify the content.
When to Use aria-readonly
Use aria-readonly when users need to view and potentially copy data but not edit it, such as form confirmation pages, order details, or locked fields in edit interfaces.
Live Demo: Edit/View Mode Toggle
Screen reader announcement: In view mode: "Full Name, read only, edit text, Jane Smith". You can still tab to these fields and select/copy text. In edit mode: "Full Name, edit text".
Attribute Values
trueThe element is not editable, but users can still focus on it, select text, and copy content. The value is still submitted with the form.
false(default)The element is editable (assuming the role supports editing). This is the default when the attribute is not present.
readonly vs disabled
aria-readonly="true"
- ✓ Can receive focus
- ✓ Included in tab order
- ✓ Text can be selected/copied
- ✓ Value submitted with form
- ✓ Operable (for custom widgets)
- ✗ Cannot edit value
Use when: Users need to view, copy, or reference the value.
disabled / aria-disabled="true"
- ✗ Cannot receive focus
- ✗ Not in tab order
- ✗ Text cannot be selected
- ✗ Value NOT submitted
- ✗ Not operable at all
- ✗ Cannot edit value
Use when: The field is completely unavailable or not applicable.
Code Examples
Basic Usage
<!-- Basic aria-readonly usage -->
<!-- Read-only input field -->
<label for="order-id">Order ID</label>
<input
type="text"
id="order-id"
value="ORD-2024-001234"
aria-readonly="true"
readonly
/>
<!-- Screen reader announces: "Order ID, read only, edit text, ORD-2024-001234" -->
<!-- Read-only textarea -->
<label for="terms">Terms and Conditions</label>
<textarea
id="terms"
aria-readonly="true"
readonly
>
By using this service, you agree to our terms...
</textarea>readonly vs disabled
<!-- aria-readonly vs disabled - KEY DIFFERENCE -->
<!-- READONLY: Can focus, can select/copy, IS in tab order -->
<input
type="text"
value="You can select and copy this text"
aria-readonly="true"
readonly
tabindex="0"
/>
<!--
• User CAN focus this field
• User CAN select and copy text
• User CANNOT edit the value
• Value IS submitted with form
• Included in tab navigation
-->
<!-- DISABLED: Cannot focus, cannot select, NOT in tab order -->
<input
type="text"
value="You cannot interact with this"
disabled
/>
<!--
• User CANNOT focus this field
• User CANNOT select or copy text
• User CANNOT edit the value
• Value is NOT submitted with form
• Excluded from tab navigation
-->
<!-- Choose wisely based on user needs! -->Custom Widgets
<!-- Custom widgets with aria-readonly -->
<!-- Read-only custom combobox -->
<div
role="combobox"
aria-readonly="true"
aria-expanded="false"
aria-labelledby="country-label"
tabindex="0"
>
<span id="country-label">Country</span>
<span>United States (locked)</span>
</div>
<!-- Read-only grid cell -->
<div role="grid" aria-label="Data Table">
<div role="row">
<div role="gridcell" aria-readonly="true">
Cell value (cannot be edited)
</div>
<div role="gridcell">
Editable cell
</div>
</div>
</div>
<!-- Read-only slider (display only) -->
<div
role="slider"
aria-readonly="true"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="75"
aria-label="Progress"
tabindex="0"
>
75%
</div>Edit Mode Toggle
<!-- Toggle between read and edit mode -->
<form id="profile-form">
<div class="form-field">
<label for="username">Username</label>
<input
type="text"
id="username"
value="johndoe"
aria-readonly="true"
readonly
/>
</div>
<div class="form-field">
<label for="email">Email</label>
<input
type="email"
id="email"
value="john@example.com"
aria-readonly="true"
readonly
/>
</div>
<button
type="button"
onclick="toggleEditMode()"
aria-pressed="false"
>
Edit Profile
</button>
</form>
<script>
function toggleEditMode() {
const inputs = document.querySelectorAll('#profile-form input');
const button = document.querySelector('button');
const isEditing = button.getAttribute('aria-pressed') === 'true';
inputs.forEach(input => {
if (isEditing) {
input.setAttribute('readonly', '');
input.setAttribute('aria-readonly', 'true');
} else {
input.removeAttribute('readonly');
input.setAttribute('aria-readonly', 'false');
}
});
button.setAttribute('aria-pressed', !isEditing);
button.textContent = isEditing ? 'Edit Profile' : 'Save Changes';
}
</script>React Component
// React component with readonly toggle
import { useState } from 'react';
function ReadonlyInput({
label,
value,
isReadonly,
onChange,
...props
}) {
return (
<div className="form-field">
<label htmlFor={props.id}>{label}</label>
<input
{...props}
value={value}
onChange={onChange}
readOnly={isReadonly}
aria-readonly={isReadonly}
className={isReadonly ? 'readonly-field' : 'editable-field'}
/>
</div>
);
}
function EditableProfileForm() {
const [isEditMode, setIsEditMode] = useState(false);
const [profile, setProfile] = useState({
name: 'John Doe',
email: 'john@example.com',
phone: '+1 (555) 123-4567'
});
const handleChange = (field) => (e) => {
setProfile(prev => ({ ...prev, [field]: e.target.value }));
};
return (
<form>
<ReadonlyInput
id="name"
label="Full Name"
value={profile.name}
isReadonly={!isEditMode}
onChange={handleChange('name')}
/>
<ReadonlyInput
id="email"
type="email"
label="Email Address"
value={profile.email}
isReadonly={!isEditMode}
onChange={handleChange('email')}
/>
<ReadonlyInput
id="phone"
type="tel"
label="Phone Number"
value={profile.phone}
isReadonly={!isEditMode}
onChange={handleChange('phone')}
/>
<button
type="button"
onClick={() => setIsEditMode(!isEditMode)}
aria-pressed={isEditMode}
>
{isEditMode ? 'Save Changes' : 'Edit Profile'}
</button>
{/* Announce mode change to screen readers */}
<div
role="status"
aria-live="polite"
className="sr-only"
>
{isEditMode ? 'Edit mode enabled' : 'View mode enabled'}
</div>
</form>
);
}Best Practices
Use aria-readonly when users need to view and copy but not edit data
Pair aria-readonly with the HTML readonly attribute for native inputs
Visually distinguish readonly fields from editable ones (different background, border)
Announce mode changes with aria-live regions when toggling edit modes
Keep readonly fields in the tab order so keyboard users can access them
Don't use aria-readonly when you mean disabled—they serve different purposes
Don't use readonly for fields that will never be editable—consider static text instead
Don't forget to style readonly fields differently for visual users
Don't prevent text selection in readonly fields—that defeats their purpose

