Skip to content

Aria In Jsx

DodaTech 3 min read

title: "ARIA Attributes in JSX" weight: 13 description: "Learn how to apply ARIA attributes conditionally in JSX, manage dynamic ARIA states with React state, use aria-expanded, aria-selected, and aria-current, and avoid common ARIA-in-React pitfalls." date: 2026-06-28 lastmod: 2026-06-28 tags: [accessibility, react]


ARIA attributes in JSX should be applied conditionally based on component state, using React state variables to control attributes like aria-expanded (open/closed), aria-selected (selected/unselected), and aria-current (current page), with proper re-rendering on state changes.

## What You'll Learn

You will apply ARIA attributes in JSX, manage dynamic ARIA values with React state, use conditional rendering for ARIA, and avoid common mistakes like mixing ARIA with semantic HTML.

## Why It Matters

ARIA attributes must reflect the current state of the component. An accordion section that is expanded should have aria-expanded=true. If the state changes, the ARIA attribute must update. React's state management makes this natural.

## Real-World Use

A React accordion component uses state.isOpen to control aria-expanded on the button. When the user clicks, isOpen toggles, React re-renders, and the button's aria-expanded updates automatically. Screen reader users hear the correct state.

## ARIA State Management

```mermaid
flowchart TD
  A[Component State] --> B[ARIA Attribute]
  B --> C[Screen Reader]
  C --> D{Renders correctly?}
  D -->|Yes| E[Accessible]
  D -->|No| F[Update ARIA binding]
  F --> A

Conditional ARIA in JSX

Use state to control ARIA attributes and spread syntax for cleaner code.

function Accordion({ title, children }) {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button
        onClick={() => setIsOpen(!isOpen)}
        aria-expanded={isOpen}
        aria-controls="accordion-content"
      >
        {title}
        <span aria-hidden={true}>{isOpen ? '-' : '+'}</span>
      </button>
      <div
        id="accordion-content"
        role="region"
        hidden={!isOpen}
      >
        {children}
      </div>
    </div>
  );
}
// Tab component with ARIA state management
function TabPanel({ tabs, activeTab, onTabChange }) {
  return (
    <div>
      <div role="tablist" aria-label="Content tabs">
        {tabs.map((tab, index) => (
          <button
            key={index}
            role="tab"
            aria-selected={activeTab === index}
            aria-controls={`panel-${index}`}
            onClick={() => onTabChange(index)}
          >
            {tab.label}
          </button>
        ))}
      </div>

      {tabs.map((tab, index) => (
        <div
          key={index}
          id={`panel-${index}`}
          role="tabpanel"
          hidden={activeTab !== index}
        >
          {tab.content}
        </div>
      ))}
    </div>
  );
}
// Navigation with aria-current
function Nav({ currentPage }) {
  const pages = ['Home', 'Products', 'About'];

  return (
    <nav aria-label="Main">
      <ul>
        {pages.map(page => (
          <li key={page}>
            <a
              href={`/${page.toLowerCase()}`}
              aria-current={currentPage === page ? 'page' : undefined}
            >
              {page}
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}

Common Mistakes

  • Hard-coding ARIA attributes instead of making them conditional
  • Forgetting to update ARIA attributes when state changes
  • Applying ARIA roles to semantic HTML elements that already have them
  • Using aria-selected incorrectly on non-selectable elements
  • Mixing ARIA with incorrect state values
  • Not using aria-controls to associate controls with their targets
  • Setting aria-hidden on focusable elements

Practice and Challenge

Practice 1: Add aria-expanded to a collapsible component. Practice 2: Create a tab panel with proper aria-selected and aria-controls. Practice 3: Add aria-current to navigation links. Practice 4: Fix an accordion that hard-codes aria-expanded. Practice 5: Create a live region that announces state changes.

Challenge: Build an accessible dropdown menu component with multiple ARIA attributes: aria-expanded on the trigger, role="listbox" on the menu, aria-activedescendant for keyboard navigation, and aria-selected on menu items. Use React state to manage all ARIA values dynamically.

FAQ

Should I use ARIA or semantic HTML?

Use semantic HTML first. Add ARIA only when the native element does not provide the needed accessibility behavior.

How do I conditionally apply ARIA in JSX?

Use JavaScript expressions inside JSX: aria-expanded={isOpen} will render aria-expanded='true' or 'false'.

Can I use aria- attributes on any element?

Yes, but prefer using semantic HTML elements and only add ARIA when necessary.

How do I handle complex ARIA states?

Use useReducer for complex state that affects multiple ARIA attributes.

Does React warn about incorrect ARIA?

React does not validate ARIA usage. Use eslint-plugin-jsx-a11y for ARIA linting.

How do I add ARIA to third-party React components?

Wrap the component and pass aria-* props. Some components support aria props natively.

Mini Project

Create a React ARIA pattern library with three components that use conditional ARIA: Accordion (aria-expanded), Tab Panel (aria-selected, aria-controls, role="tablist"), and Menu Button (aria-expanded, aria-haspopup, role="menu"). Each component should pass axe-core testing.

What's Next

Focus Management with Hooks covers using useRef and useEffect for focus management.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro