Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTEWidget Attributes

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.

Value Type
true | false
Common Use
View/Edit Modes
Compare To
aria-disabled

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

Profile Information
View mode enabled, fields are read-only

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

true

The element is not editable, but users can still focus on it, select text, and copy content. The value is still submitted with the form.

Screen reader: "[Label], read only, edit text, [value]"
false(default)

The element is editable (assuming the role supports editing). This is the default when the attribute is not present.

Screen reader: "[Label], edit text"

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

Common Use Cases

Order confirmation details
User profile view mode
Account number display
Generated reference codes
Locked form fields
Date stamps and timestamps
Calculated totals
System-generated IDs

Related Attributes

Specifications & Resources