Skip to main content
Accessible icons ensure that all users, including those using assistive technologies, can understand and interact with your application effectively.

The Title Prop

The title prop adds a <title> element inside the SVG, providing a text description for screen readers:
import { FaDownload } from "react-icons/fa";

function DownloadButton() {
  return (
    <button>
      <FaDownload title="Download file" />
      Download
    </button>
  );
}
When rendered, this creates:
<svg xmlns="http://www.w3.org/2000/svg">
  <title>Download file</title>
  <!-- icon paths -->
</svg>
The title element provides accessible names for screen readers and appears as a tooltip in many browsers.

Decorative vs Meaningful Icons

Icons fall into two categories that require different accessibility approaches:
Icons that are purely visual and don’t convey unique information should be hidden from screen readers:
import { FaBeer } from "react-icons/fa";

function Question() {
  return (
    <h3>
      Lets go for a <FaBeer aria-hidden="true" />?
    </h3>
  );
}
The text “Lets go for a?” conveys the message, so the icon is decorative.

Icon Buttons

When icons are used as buttons without text, provide accessible labels:
1

Use aria-label

Add descriptive aria-label to the button element:
import { FaSearch } from "react-icons/fa";

function SearchButton() {
  return (
    <button aria-label="Search" onClick={() => {/* search logic */}}>
      <FaSearch />
    </button>
  );
}
2

Add title for tooltips

Optionally include title for visual tooltip:
import { FaSearch } from "react-icons/fa";

function SearchButton() {
  return (
    <button aria-label="Search" title="Search">
      <FaSearch />
    </button>
  );
}
3

Hide icon from screen readers

Add aria-hidden to the icon to avoid redundancy:
import { FaSearch } from "react-icons/fa";

function SearchButton() {
  return (
    <button aria-label="Search">
      <FaSearch aria-hidden="true" />
    </button>
  );
}
import { FaHeart } from "react-icons/fa";

function LikeButton() {
  return (
    <button aria-label="Like this post">
      <FaHeart aria-hidden="true" />
    </button>
  );
}

Icons with Adjacent Text

When icons accompany visible text, hide the icon from screen readers:
import { FaDownload, FaUpload } from "react-icons/fa";

function FileActions() {
  return (
    <div>
      <button>
        <FaDownload aria-hidden="true" style={{ marginRight: '8px' }} />
        Download
      </button>
      <button>
        <FaUpload aria-hidden="true" style={{ marginRight: '8px' }} />
        Upload
      </button>
    </div>
  );
}
Don’t add title or aria-label to decorative icons when text is already present - this creates redundant announcements for screen reader users.
For icon-based navigation links:
import { FaGithub, FaTwitter, FaLinkedin } from "react-icons/fa";

function SocialLinks() {
  return (
    <nav aria-label="Social media links">
      <a 
        href="https://github.com/username" 
        aria-label="GitHub profile"
        target="_blank"
        rel="noopener noreferrer"
      >
        <FaGithub aria-hidden="true" />
      </a>
      <a 
        href="https://twitter.com/username" 
        aria-label="Twitter profile"
        target="_blank"
        rel="noopener noreferrer"
      >
        <FaTwitter aria-hidden="true" />
      </a>
      <a 
        href="https://linkedin.com/in/username" 
        aria-label="LinkedIn profile"
        target="_blank"
        rel="noopener noreferrer"
      >
        <FaLinkedin aria-hidden="true" />
      </a>
    </nav>
  );
}

Status Indicators

For icons indicating status, ensure the meaning is conveyed accessibly:
import { FaCheckCircle, FaTimesCircle, FaSpinner } from "react-icons/fa";

function StatusMessage({ status, message }) {
  const icons = {
    success: FaCheckCircle,
    error: FaTimesCircle,
    loading: FaSpinner
  };
  
  const Icon = icons[status];
  
  return (
    <div role="status" aria-live="polite">
      <Icon aria-hidden="true" />
      <span>{message}</span>
    </div>
  );
}

Form Field Icons

Icons in form fields require special consideration:
import { FaUser, FaLock, FaEnvelope } from "react-icons/fa";

function LoginForm() {
  return (
    <form>
      <div>
        <label htmlFor="username">
          <FaUser aria-hidden="true" />
          Username
        </label>
        <input id="username" type="text" />
      </div>
      
      <div>
        <label htmlFor="email">
          <FaEnvelope aria-hidden="true" />
          Email
        </label>
        <input id="email" type="email" />
      </div>
      
      <div>
        <label htmlFor="password">
          <FaLock aria-hidden="true" />
          Password
        </label>
        <input id="password" type="password" />
      </div>
    </form>
  );
}
Always pair form field icons with visible labels. Icons alone are not sufficient for accessibility.

Icon-Based Navigation

For icon-heavy navigation, provide multiple ways to understand the interface:
import { FaHome, FaUser, FaCog, FaBell } from "react-icons/fa";

function TabNavigation() {
  return (
    <nav aria-label="Main navigation">
      <ul role="tablist">
        <li role="presentation">
          <button role="tab" aria-selected="true" aria-label="Home">
            <FaHome aria-hidden="true" />
            <span>Home</span>
          </button>
        </li>
        <li role="presentation">
          <button role="tab" aria-selected="false" aria-label="Profile">
            <FaUser aria-hidden="true" />
            <span>Profile</span>
          </button>
        </li>
        <li role="presentation">
          <button role="tab" aria-selected="false" aria-label="Settings">
            <FaCog aria-hidden="true" />
            <span>Settings</span>
          </button>
        </li>
        <li role="presentation">
          <button role="tab" aria-selected="false" aria-label="Notifications">
            <FaBell aria-hidden="true" />
            <span>Notifications</span>
          </button>
        </li>
      </ul>
    </nav>
  );
}

Color and Contrast

Never rely on color alone to convey information. Icons should be distinguishable by shape, not just color.
Ensure icons meet WCAG contrast requirements:
import { FaExclamationTriangle, FaInfoCircle } from "react-icons/fa";

function Alerts() {
  return (
    <div>
      {/* ✓ Good: Combines color with icon shape and text */}
      <div role="alert">
        <FaExclamationTriangle 
          aria-hidden="true" 
          color="#dc2626" 
        />
        <span>Error: Please check your input</span>
      </div>
      
      {/* ✓ Good: High contrast, distinctive shape */}
      <div role="status">
        <FaInfoCircle 
          aria-hidden="true" 
          color="#2563eb" 
        />
        <span>Info: Your changes have been saved</span>
      </div>
    </div>
  );
}

Accessibility Checklist

✓ Add aria-hidden="true"✓ Ensure adjacent text provides full context✓ Don’t add title or aria-label
✓ Add aria-label to the parent interactive element✓ Or provide visible text label✓ Consider adding title for tooltips✓ Add aria-hidden="true" to avoid redundancy when label is present
✓ Use role="status" or role="alert"✓ Consider aria-live regions for dynamic updates✓ Include text description, not just the icon
✓ Ensure 3:1 contrast ratio for UI components (WCAG AA)✓ Don’t rely on color alone to convey meaning✓ Test with color blindness simulators

Testing Accessibility

Test your icon implementation with:
  • Screen readers: NVDA (Windows), JAWS (Windows), VoiceOver (macOS/iOS)
  • Keyboard navigation: Ensure all icon buttons are keyboard accessible
  • Browser extensions: axe DevTools, WAVE, Lighthouse
  • Automated testing: jest-axe, pa11y, Playwright accessibility tests
// Example: Testing with jest-axe
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import { FaDownload } from 'react-icons/fa';

expect.extend(toHaveNoViolations);

test('download button is accessible', async () => {
  const { container } = render(
    <button aria-label="Download file">
      <FaDownload aria-hidden="true" />
    </button>
  );
  
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Next Steps

Customizing Icons

Learn how to customize icon appearance

Styling

Apply global styles with IconContext