Claude
Skills
Sign in
Back

base-headless

Included with Lifetime
$97 forever

MUI Base (unstyled/headless) components and hooks — useButton, useInput, useMenu, useSlider for building custom UI

Design

What this skill does


# MUI Base (Headless/Unstyled) Components

## What is MUI Base?

MUI Base (`@mui/base`) is a library of headless (unstyled) React components and hooks. Unlike Material UI, which ships with Material Design styles baked in, MUI Base provides only the logic, state management, accessibility, and keyboard interactions -- zero CSS. You bring your own styles using Tailwind, CSS Modules, styled-components, vanilla CSS, or any approach you prefer.

```bash
npm install @mui/base
# or
pnpm add @mui/base
```

Key characteristics:

- **Zero default styles** -- components render semantic HTML with no class names or CSS
- **Hooks-first API** -- every component has a corresponding hook (`useButton`, `useInput`, etc.) for maximum flexibility
- **WAI-ARIA compliant** -- accessibility is handled internally (focus management, keyboard navigation, ARIA attributes)
- **Small bundle** -- no theme provider, no emotion/styled-engine dependency
- **Composable** -- hooks return prop-getters (`getRootProps`, `getInputProps`) that you spread onto your own elements

## When to Use MUI Base

| Scenario | Use MUI Base? |
|----------|--------------|
| Building a custom design system (not Material Design) | Yes |
| Integrating with Tailwind CSS or other utility-first CSS | Yes |
| Need maximum control over rendered HTML and styles | Yes |
| Want Material Design out of the box | No -- use Material UI |
| Need a quick prototype with default styling | No -- use Material UI or Joy UI |
| Building a white-label product with multiple brand themes | Yes |

## Core Hooks

### useButton

Provides button behavior including click handling, disabled state, focus-visible detection, and keyboard activation.

```tsx
import { useButton } from '@mui/base/useButton';
import { useRef } from 'react';
import clsx from 'clsx';

interface CustomButtonProps {
  children: React.ReactNode;
  disabled?: boolean;
  onClick?: React.MouseEventHandler;
  variant?: 'primary' | 'secondary' | 'ghost';
}

function CustomButton({ children, disabled, onClick, variant = 'primary' }: CustomButtonProps) {
  const buttonRef = useRef<HTMLButtonElement>(null);
  const { getRootProps, active, disabled: isDisabled, focusVisible } = useButton({
    disabled,
    rootRef: buttonRef,
  });

  return (
    <button
      {...getRootProps({ onClick })}
      className={clsx(
        'px-4 py-2 rounded-lg font-medium transition-all',
        variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
        variant === 'secondary' && 'bg-gray-200 text-gray-800 hover:bg-gray-300',
        variant === 'ghost' && 'bg-transparent text-gray-600 hover:bg-gray-100',
        active && 'scale-95',
        isDisabled && 'opacity-50 cursor-not-allowed',
        focusVisible && 'ring-2 ring-blue-400 ring-offset-2',
      )}
    >
      {children}
    </button>
  );
}
```

**Returned values from `useButton`:**

| Property | Type | Description |
|----------|------|-------------|
| `getRootProps` | `(externalProps?) => props` | Spread onto the root element (button/anchor) |
| `active` | `boolean` | True while the button is being pressed |
| `disabled` | `boolean` | Reflects the disabled state |
| `focusVisible` | `boolean` | True when focused via keyboard (not mouse) |

### useInput

Manages input state including focus, error, and adornment support.

```tsx
import { useInput } from '@mui/base/useInput';
import { useRef } from 'react';
import clsx from 'clsx';

interface CustomInputProps {
  placeholder?: string;
  value?: string;
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
  error?: boolean;
  disabled?: boolean;
  startAdornment?: React.ReactNode;
  endAdornment?: React.ReactNode;
}

function CustomInput({
  placeholder,
  value,
  onChange,
  error,
  disabled,
  startAdornment,
  endAdornment,
}: CustomInputProps) {
  const inputRef = useRef<HTMLInputElement>(null);
  const {
    getRootProps,
    getInputProps,
    focused,
    error: hasError,
    disabled: isDisabled,
  } = useInput({
    value,
    onChange,
    error,
    disabled,
    inputRef,
  });

  return (
    <div
      {...getRootProps()}
      className={clsx(
        'flex items-center gap-2 px-3 py-2 border rounded-lg transition-colors',
        focused && 'border-blue-500 ring-1 ring-blue-500',
        hasError && 'border-red-500 ring-1 ring-red-500',
        isDisabled && 'bg-gray-100 opacity-60',
        !focused && !hasError && 'border-gray-300 hover:border-gray-400',
      )}
    >
      {startAdornment}
      <input
        {...getInputProps()}
        placeholder={placeholder}
        className="flex-1 outline-none bg-transparent text-sm"
      />
      {endAdornment}
    </div>
  );
}

// Usage
<CustomInput
  placeholder="Search..."
  startAdornment={<SearchIcon className="w-4 h-4 text-gray-400" />}
  endAdornment={<kbd className="text-xs text-gray-400">Ctrl+K</kbd>}
/>
```

**Returned values from `useInput`:**

| Property | Type | Description |
|----------|------|-------------|
| `getRootProps` | `(externalProps?) => props` | Spread onto the wrapper element |
| `getInputProps` | `(externalProps?) => props` | Spread onto the `<input>` element |
| `focused` | `boolean` | True when the input has focus |
| `error` | `boolean` | Reflects the error state |
| `disabled` | `boolean` | Reflects the disabled state |
| `value` | `string` | Current input value (controlled) |

### useMenu / useMenuItem

Build accessible dropdown menus with keyboard navigation, highlight management, and open/close state.

```tsx
import { useMenu } from '@mui/base/useMenu';
import { useMenuItem } from '@mui/base/useMenuItem';
import { useDropdown, DropdownContext } from '@mui/base/useDropdown';
import { useMenuButton } from '@mui/base/useMenuButton';
import { useRef, useState } from 'react';
import clsx from 'clsx';

function MenuButton({ children }: { children: React.ReactNode }) {
  const buttonRef = useRef<HTMLButtonElement>(null);
  const { getRootProps, active } = useMenuButton({ rootRef: buttonRef });

  return (
    <button
      {...getRootProps()}
      className={clsx(
        'px-4 py-2 bg-white border rounded-lg shadow-sm hover:bg-gray-50',
        active && 'bg-gray-100',
      )}
    >
      {children}
    </button>
  );
}

function MenuItem({
  children,
  onClick,
  disabled,
}: {
  children: React.ReactNode;
  onClick?: () => void;
  disabled?: boolean;
}) {
  const ref = useRef<HTMLLIElement>(null);
  const { getRootProps, highlighted, disabled: isDisabled } = useMenuItem({
    rootRef: ref,
    onClick,
    disabled,
  });

  return (
    <li
      {...getRootProps()}
      className={clsx(
        'px-4 py-2 text-sm cursor-pointer transition-colors',
        highlighted && 'bg-blue-50 text-blue-700',
        isDisabled && 'text-gray-400 cursor-not-allowed',
        !highlighted && !isDisabled && 'text-gray-700 hover:bg-gray-50',
      )}
    >
      {children}
    </li>
  );
}

function Menu({ children }: { children: React.ReactNode }) {
  const listboxRef = useRef<HTMLUListElement>(null);
  const { getListboxProps, open } = useMenu({ listboxRef });

  if (!open) return null;

  return (
    <ul
      {...getListboxProps()}
      className="absolute mt-1 w-56 bg-white border rounded-lg shadow-lg py-1 z-50"
    >
      {children}
    </ul>
  );
}

// Full dropdown composition
function CustomDropdown() {
  const { contextValue } = useDropdown();

  return (
    <DropdownContext.Provider value={contextValue}>
      <div className="relative inline-block">
        <MenuButton>Actions</MenuButton>
        <Menu>
          <MenuItem onClick={() => console.log('Edit')}>Edit</MenuItem>
          <MenuItem onClick={() => console.log('Duplicate')}>Duplicate</MenuItem>
          <MenuItem disabled>Archive</MenuItem>
          <MenuItem onClick={() => console.log('Delete')}>Delete</MenuItem>
        </Menu>
      </div>
    </DropdownContext.Provider>
  );
}
```

**Key props from `useMenu`:**

| Property | Type | Description |
|----------|------|-------------|
| `getLis

Related in Design