Icon buttons

Icon buttons help people take minor actions with one tap

Usage

Use icon buttons to display actions in a compact layout. Icon buttons can represent opening actions such as opening an overflow menu or search, or represent binary actions that can be toggled on and off, such as favorite or bookmark.

Icon buttons can be grouped together or they can stand alone.

Instalation

Run the following command if is not already installed:

npm i @radix-ui/react-slot

Copy and paste the following code into your project.

import * as React from 'react'
import { Slot, Slottable } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'

import { cn } from '@/lib/utils'

const iconButtonVariants = cva(
  'shrink-0 relative group/button h-10 w-10 overflow-hidden grid place-items-center rounded-xl transition outline-none disabled:pointer-events-none disabled:text-onSurface/38 active:scale-[0.98]',
  {
    variants: {
      variant: {
        filled: 'bg-primary text-onPrimary disabled:bg-onSurface/12',
        tonal:
          'bg-secondaryContainer text-onSecondaryContainer disabled:bg-onSurface/12',
        outlined:
          'border border-outline text-onSurfaceVariant disabled:border-onSurface/12',
        standard: 'text-onSurfaceVariant',
      },
    },
    defaultVariants: {
      variant: 'filled',
    },
  }
)

const stateLayerVariants = cva(
  'transition-opacity absolute inset-0 z-0 opacity-0 group-hover/button:opacity-8 group-focus/button:opacity-12 group-active/button:opacity-12',
  {
    variants: {
      variant: {
        filled: 'bg-onPrimary',
        tonal: 'bg-onSecondaryContainer',
        outlined: 'bg-onSurfaceVariant',
        standard: 'bg-onSurfaceVariant',
      },
    },
    defaultVariants: {
      variant: 'filled',
    },
  }
)

export interface IconButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof iconButtonVariants> {
  asChild?: boolean
  disableStateLayer?: boolean
}

const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
  (
    { className, variant, asChild, disableStateLayer, children, ...props },
    ref
  ) => {
    const Comp = asChild ? Slot : 'button'

    return (
      <Comp
        ref={ref}
        className={cn(iconButtonVariants({ variant, className }))}
        {...props}
      >
        <Slottable>{children}</Slottable>
        {!disableStateLayer && (
          <span className={cn(stateLayerVariants({ variant }))} />
        )}
      </Comp>
    )
  }
)

IconButton.displayName = 'IconButton'

export { IconButton, iconButtonVariants, stateLayerVariants }

Update the import paths to match your project setup.

Examples

Filled icon button

Filled icon buttons have higher visual impact and are best for high emphasis actions.

import { Icon } from '@/components/ui/icon'
import { IconButton } from '@/components/ui/icon-button'

export const FilledIconButton = () => {
  return (
    <IconButton>
      <Icon symbol="settings" />
    </IconButton>
  )
}

Filled tonal icon button

Filled tonal icon buttons are a middle ground between filled and outlined icon buttons. They’re useful in contexts where the button requires slightly more emphasis than an outline would give, such as a secondary action paired with a high emphasis action.

import { Icon } from '@/components/ui/icon'
import { IconButton } from '@/components/ui/icon-button'

export const FilledTonalIconButton = () => {
  return (
    <IconButton variant="tonal">
      <Icon symbol="settings" />
    </IconButton>
  )
}

Outlined icon button

Outlined icon buttons are medium-emphasis buttons. They’re useful when an icon button needs more emphasis than a standard icon button but less than a filled or filled tonal icon button.

import { Icon } from '@/components/ui/icon'
import { IconButton } from '@/components/ui/icon-button'

export const OutlinedIconButton = () => {
  return (
    <IconButton variant="outlined">
      <Icon symbol="settings" />
    </IconButton>
  )
}

Standard icon button

Standard icon buttons are small-emphasis buttons. They’re useful when an icon button needs less emphasis than a contained button.

import { Icon } from '@/components/ui/icon'
import { IconButton } from '@/components/ui/icon-button'

export const StandardIconButton = () => {
  return (
    <IconButton variant="standard">
      <Icon symbol="settings" />
    </IconButton>
  )
}