Floating Action Buttons

Floating action buttons (FABs) help people take primary actions

Usage

Use a FAB for the most important action on a screen. The FAB appears in front of all other content on screen, and is recognizable for its rounded shape and icon in the center.

Only use a FAB for presenting a screen’s primary action.

The FAB can be aligned left, center, or right. It can be positioned above the navigation bar, or nested within the bar.

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 fabVariants = cva(
  'relative group w-fit z-0 shadow-md flex items-center justify-center overflow-hidden transition-all outline-none disabled:pointer-events-none disabled:text-onSurface/38 disabled:shadow-none disabled:bg-onSurface/12 hover:shadow-sm focus-visible:shadow-sm active:shadow-xs active:scale-[0.98]',
  {
    variants: {
      size: {
        small: 'h-10 min-w-[40px] px-2 rounded-md',
        default: 'h-14 min-w-[56px] px-4 rounded-lg',
        large: 'h-24 min-w-[96px] px-[30px] rounded-xl',
      },
      variant: {
        primary: 'bg-primaryContainer text-onPrimaryContainer',
        surface: 'bg-surfaceContainer text-primary',
        secondary: 'bg-secondaryContainer text-onSecondaryContainer',
        tertiary: 'bg-tertiaryContainer text-onTertiaryContainer',
      },
    },
    defaultVariants: {
      size: 'default',
      variant: 'primary',
    },
  }
)

const stateLayerVariants = cva(
  'transition-opacity absolute inset-0 z-[-1] opacity-0 group-hover:opacity-8 group-focus:opacity-12 group-active:opacity-12',
  {
    variants: {
      variant: {
        primary: 'bg-onPrimaryContainer',
        surface: 'bg-primary',
        secondary: 'bg-onSecondaryContainer',
        tertiary: 'bg-onTertiaryContainer',
      },
    },
    defaultVariants: {
      variant: 'primary',
    },
  }
)

export interface FabProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof fabVariants> {
  asChild?: boolean
}

const FabRoot = React.forwardRef<HTMLButtonElement, FabProps>(
  ({ className, variant, size, asChild = false, children, ...props }, ref) => {
    const Comp = asChild ? Slot : 'button'

    return (
      <Comp
        ref={ref}
        className={cn(fabVariants({ size, variant, className }))}
        {...props}
      >
        <Slottable>{children}</Slottable>
        <span className={cn(stateLayerVariants({ variant }))} />
      </Comp>
    )
  }
)
FabRoot.displayName = 'FabRoot'

const FabIcon = React.forwardRef<
  HTMLSpanElement,
  React.HTMLAttributes<HTMLSpanElement>
>(({ className, children, ...props }, ref) => {
  return (
    <span
      ref={ref}
      className={cn('flex aspect-square shrink-0', className)}
      {...props}
    >
      {children}
    </span>
  )
})
FabIcon.displayName = 'FabIcon'

const FabLabel = React.forwardRef<
  HTMLSpanElement,
  React.HTMLAttributes<HTMLSpanElement>
>(({ className, ...props }, ref) => (
  <span ref={ref} className={cn('px-2 text-label-lg', className)} {...props} />
))
FabLabel.displayName = 'FabLabel'

const Fab = Object.assign(FabRoot, {
  Icon: FabIcon,
  Label: FabLabel,
})

export { Fab, FabIcon, FabLabel, fabVariants, stateLayerVariants }

Update the import paths to match your project setup.

Examples

Default FAB

Use a FAB to represent the screen’s primary action.

import { Fab } from '@/components/ui/fab'
import { Icon } from '@/components/ui/icon'

export const DefaultFAB = () => {
  return (
    <Fab>
      <Fab.Icon>
        <Icon symbol="palette" />
      </Fab.Icon>
    </Fab>
  )
}

Small FAB

A small FAB is used for a secondary, supporting action.

import { Fab } from '@/components/ui/fab'
import { Icon } from '@/components/ui/icon'

export const SmallFAB = () => {
  return (
    <Fab size="small">
      <Fab.Icon>
        <Icon symbol="palette" />
      </Fab.Icon>
    </Fab>
  )
}

Large FAB

A large FAB is useful when the layout calls for a clear and prominent primary action.

import { Fab } from '@/components/ui/fab'
import { Icon } from '@/components/ui/icon'

export const LargeFAB = () => {
  return (
    <Fab size="large">
      <Fab.Icon>
        <Icon symbol="palette" className="text-[32px]" />
      </Fab.Icon>
    </Fab>
  )
}

Extended FAB

Extended FAB is usually used in long scrollable screens.

import { Fab } from '@/components/ui/fab'
import { Icon } from '@/components/ui/icon'

export const ExtendedFAB = () => {
  return (
    <Fab>
      <Fab.Icon>
        <Icon symbol="edit" />
      </Fab.Icon>
      <Fab.Label>New task</Fab.Label>
    </Fab>
  )
}

Responsive Extended FAB

import { Fab } from '@/components/ui/fab'
import { Icon } from '@/components/ui/icon'

export const ResponsiveExtendedFAB = () => {
  return (
    <Fab variant="tertiary">
      <Fab.Icon>
        <Icon symbol="edit" />
      </Fab.Icon>
      <Fab.Label className="hidden sm:block">New task</Fab.Label>
    </Fab>
  )
}