Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTERelationship Attributes

aria-owns

Identifies an element (or elements) to define a visual, functional, or contextual parent-child relationship when the DOM hierarchy cannot represent it.

Value Type
ID Reference (List)
Global Attribute
Yes
Common Use
Portals, Popups, Detached menus

Overview

The aria-owns attribute identifies elements that should be considered children of the current element, even when they aren't DOM descendants. This is essential for modern UI patterns like portals.

When using React portals, Vue teleport, or rendering popups in a separate DOM location (for z-index or overflow handling), aria-owns maintains the correct accessibility tree structure.

Important: Only use aria-owns when the DOM hierarchy doesn't match the visual/functional relationship. Prefer proper DOM nesting when possible.

Visual Demo: Parent-Child Relationship

Without aria-owns

Button (DOM parent: body)

Menu (DOM parent: portal-root)

  • Item 1
  • Item 2

AT doesn't know menu belongs to button

With aria-owns

Button (aria-owns="demo-menu")

Menu in portal (id="demo-menu")

AT knows menu is owned by button

aria-owns creates a logical parent-child relationship in the accessibility tree even when DOM structure differs. This is essential for portal-based popups and menus.

Code Examples

Basic Usage

<!-- aria-owns creates parent-child relationship when DOM doesn't -->

<!-- Menu button with detached popup (e.g., in portal) -->
<button
  aria-haspopup="menu"
  aria-expanded="true"
  aria-owns="user-menu"
>
  User Settings
</button>

<!-- Menu is in a portal/different DOM location -->
<div id="portal-root">
  <ul id="user-menu" role="menu">
    <li role="menuitem">Profile</li>
    <li role="menuitem">Settings</li>
    <li role="menuitem">Logout</li>
  </ul>
</div>

<!-- aria-owns tells AT that user-menu belongs to the button -->

Combobox Pattern

<!-- Combobox with listbox in portal -->

<div role="combobox" 
     aria-expanded="true"
     aria-haspopup="listbox"
     aria-owns="suggestions-list">
  <input type="text" 
         aria-autocomplete="list"
         aria-controls="suggestions-list" />
</div>

<!-- Listbox rendered in portal for z-index/overflow -->
<div id="portal">
  <ul id="suggestions-list" role="listbox">
    <li role="option" aria-selected="true">Option 1</li>
    <li role="option">Option 2</li>
    <li role="option">Option 3</li>
  </ul>
</div>

<!-- Without aria-owns, AT wouldn't know the listbox 
     belongs to the combobox -->

Tree with Lazy Loading

<!-- Tree with dynamically loaded branches -->

<ul role="tree" aria-owns="lazy-branch">
  <li role="treeitem" aria-expanded="true">
    Root Item
    <ul role="group">
      <li role="treeitem">Child 1</li>
      <li role="treeitem">Child 2</li>
    </ul>
  </li>
</ul>

<!-- Lazy-loaded branch rendered elsewhere -->
<ul id="lazy-branch" role="group">
  <li role="treeitem">Lazy Child A</li>
  <li role="treeitem">Lazy Child B</li>
</ul>

Multiple Owned Elements

<!-- Owning multiple elements -->

<div role="listbox" 
     aria-owns="option-a option-b option-c"
     aria-label="Select items">
  <!-- Visual container may be empty, owned items elsewhere -->
</div>

<!-- Options rendered in different containers -->
<div class="column-1">
  <div id="option-a" role="option">Item A</div>
  <div id="option-b" role="option">Item B</div>
</div>
<div class="column-2">
  <div id="option-c" role="option">Item C</div>
</div>

<!-- Space-separated IDs for multiple owned elements -->

React with Portals

// React components with aria-owns for portals
import { useState, useId } from 'react';
import { createPortal } from 'react-dom';

// Dropdown menu with portal
function DropdownMenu({ trigger, children }) {
  const [isOpen, setIsOpen] = useState(false);
  const menuId = useId();

  return (
    <>
      <button
        aria-haspopup="menu"
        aria-expanded={isOpen}
        aria-owns={isOpen ? menuId : undefined}
        onClick={() => setIsOpen(!isOpen)}
      >
        {trigger}
      </button>
      
      {isOpen && createPortal(
        <ul
          id={menuId}
          role="menu"
          onKeyDown={(e) => {
            if (e.key === 'Escape') setIsOpen(false);
          }}
        >
          {children}
        </ul>,
        document.body
      )}
    </>
  );
}

// Combobox with suggestions in portal
function Combobox({ options, onSelect }) {
  const [isOpen, setIsOpen] = useState(false);
  const [inputValue, setInputValue] = useState('');
  const listboxId = useId();

  const filteredOptions = options.filter(opt =>
    opt.toLowerCase().includes(inputValue.toLowerCase())
  );

  return (
    <div
      role="combobox"
      aria-expanded={isOpen}
      aria-haspopup="listbox"
      aria-owns={isOpen ? listboxId : undefined}
    >
      <input
        type="text"
        value={inputValue}
        onChange={(e) => {
          setInputValue(e.target.value);
          setIsOpen(true);
        }}
        onFocus={() => setIsOpen(true)}
        aria-autocomplete="list"
        aria-controls={listboxId}
      />
      
      {isOpen && createPortal(
        <ul id={listboxId} role="listbox">
          {filteredOptions.map((option, i) => (
            <li
              key={i}
              role="option"
              onClick={() => {
                onSelect(option);
                setInputValue(option);
                setIsOpen(false);
              }}
            >
              {option}
            </li>
          ))}
        </ul>,
        document.body
      )}
    </div>
  );
}

// Dialog that owns its content through portal
function Dialog({ isOpen, title, children, onClose }) {
  const contentId = useId();

  if (!isOpen) return null;

  return createPortal(
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby={contentId + '-title'}
      aria-owns={contentId}
    >
      <div id={contentId}>
        <h2 id={contentId + '-title'}>{title}</h2>
        {children}
        <button onClick={onClose}>Close</button>
      </div>
    </div>,
    document.body
  );
}

Related Attributes

Specifications & Resources