• Dec 1, 2025
  • 4 min read

Building accessible custom components with Material UI and React

React Material UI accessible custom components

Introduction

Material UI (MUI) provides a powerful set of accessible components out of the box, but most real-world applications need custom behavior, branding, and layout adjustments. The challenge is extending or customizing MUI components without breaking accessibility.

Many teams unknowingly remove ARIA attributes, misconfigure roles, or introduce keyboard traps when customizing MUI. This article explores how to safely extend MUI components, build accessible custom components, and ensure your UI remains compliant with WCAG guidelines.

You’ll learn:

  • How MUI handles accessibility internally
  • How to override and extend components properly
  • Patterns for accessible custom components
  • Tools and workflows for testing accessibility

By the end, you’ll be able to build fully custom components while keeping your React apps inclusive and compliant.

1. Understanding MUI Accessibility Internals

MUI provides strong accessibility defaults:

  • Proper ARIA roles for controls
  • Keyboard navigation behavior
  • Focus management for modals, menus, dialogs
  • Labeling logic in form controls
  • WCAG-aligned color contrast defaults

Example: MUI <Button> automatically handles role="button" and keyboard activation:

import { Button } from "@mui/material";

export default function Demo() {
  return <Button variant="contained">Save</Button>;
}

When customizing, it’s essential to preserve or reapply these behaviors.

2. Extending MUI Components Safely

Using styled() API

import { styled } from "@mui/material/styles";
import Button from "@mui/material/Button";

const PrimaryButton = styled(Button)({
  padding: "12px 20px",
  fontWeight: 600,
  borderRadius: "10px"
});

export default function Demo() {
  return <PrimaryButton>Continue</PrimaryButton>;
}

Accessibility is preserved because the underlying component structure remains unchanged.

Using slotProps and componentsProps

For deeper customization:

<TextField
  label="Email"
  slotProps={{
    inputLabel: { htmlFor: "email-field" },
    input: { id: "email-field", "aria-required": "true" }
  }}
/>

Avoid this anti-pattern

❌ Replacing MUI components with plain <div> containers:

// WRONG – removes button semantics
<div onClick={handleClick}>Save</div>

3. Building Custom Components from Scratch (MUI + ARIA)

Sometimes you need a fully custom component with MUI theme tokens.

Example: Accessible Toggle Switch

import { useState } from "react";
import { Box } from "@mui/material";

export default function AccessibleToggle() {
  const [on, setOn] = useState(false);

  return (
    <Box
      role="switch"
      aria-checked={on}
      tabIndex={0}
      onClick={() => setOn(!on)}
      onKeyDown={(e) => e.key === "Enter" && setOn(!on)}
      sx={{
        width: 50,
        height: 24,
        borderRadius: 12,
        backgroundColor: on ? "primary.main" : "grey.500",
        position: "relative",
        cursor: "pointer"
      }}
    >
      <Box
        sx={{
          width: 20,
          height: 20,
          borderRadius: "50%",
          backgroundColor: "white",
          position: "absolute",
          top: 2,
          left: on ? 28 : 2,
          transition: "left 0.2s ease"
        }}
      />
    </Box>
  );
}

Key accessibility notes:

  • role="switch"
  • aria-checked
  • Supports keyboard interaction (Enter)
  • Focusable (tabIndex=0)

4. Testing Accessibility

Your custom MUI components should be tested with:

a) Axe DevTools (browser extension)

  • Highlights missing labels
  • Detects low contrast
  • Flags role mismatches

b) Lighthouse Accessibility Audit

Run in Chrome DevTools → Audits.

c) Testing Library + jest-axe

import { render } from "@testing-library/react";
import { axe } from "jest-axe";
import AccessibleToggle from "./AccessibleToggle";

test("toggle is accessible", async () => {
  const { container } = render(<AccessibleToggle />);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

5. Common Pitfalls

❌ Removing keyboard navigation

For example, converting a MUI Button into a styled Box.

❌ Not forwarding refs

MUI relies on forwarded refs for focus management.

❌ Incorrect ARIA attributes

Avoid using ARIA roles when semantic HTML already provides them.

❌ Custom components without labels

Always link <label> and input via htmlFor.

Conclusion

Material UI gives you a strong accessibility foundation, but extending it requires care. By reusing MUI’s component structure, applying ARIA roles correctly, and testing with automated tools, you can create custom UI experiences without compromising inclusivity.

Accessible components improve usability for everyone—not just users with assistive technologies—and elevate the quality of your entire React application.

References & tools

Material ui Mui accessibility React a11y Accessible components Mui override Aria patterns Accessibility testing

Elevate your digital experience

Whether you need a custom UI design, AI-powered solutions, or expert consulting, we are here to help you bring your ideas to life with precision, creativity, and the latest in AI technology.