Dropdown

An iOS-inspired morphing dropdown menu with smooth animations and submenu support.

Direction:
Anchor:
Submenu:

Overview

A morphing dropdown menu with fluid spring animations. The component transforms from a compact button into a full menu with support for multiple expansion directions, flexible positioning, and nested submenus.

Features

  • Multi-directional expansion - Opens from top, bottom, left, or right
  • Flexible positioning - Anchor to start, center, or end alignment
  • Nested submenus - Smooth transitions between menu levels
  • Spring physics - Natural, physics-based animations
  • Reduced motion support - Respects user accessibility preferences
  • Dark mode - Full theming support with design tokens
  • Controlled and uncontrolled - Works with external state or standalone

Installation

The Dropdown component is built on top of Framer Motion for animations.

Usage

Basic Example

With Submenu

API Reference

Root

Provides state and configuration context for the dropdown.

Props:

  • direction?: 'top' | 'bottom' | 'left' | 'right' - Menu expansion direction (default: 'top')
  • anchor?: 'start' | 'center' | 'end' - Anchor alignment (default: 'start'). Note: left/right directions only support 'center'
  • open?: boolean - Controlled open state
  • onOpenChange?: (open: boolean) => void - Open state change callback
  • defaultOpen?: boolean - Default open state for uncontrolled usage
  • visualDuration?: number - Spring animation duration in seconds (default: 0.25)
  • bounce?: number - Spring animation bounce (default: 0.2)
  • closeOnClickOutside?: boolean - Close when clicking outside (default: true)
  • closeOnEscape?: boolean - Close on Escape key (default: true)

Container

The morphing element that animates from button to menu.

Props:

  • buttonSize?: number | { width: number; height: number } - Closed button size (default: 40)
  • menuWidth?: number - Open menu width (default: 200)
  • menuRadius?: number - Open menu border-radius (default: 24)
  • buttonRadius?: number - Closed button border-radius (defaults to pill shape)
  • className?: string - Additional class names
  • style?: CSSProperties - Additional styles

Trigger

The content displayed when the menu is closed.

Props:

  • children: ReactNode - Trigger content
  • disabled?: boolean - Disable the trigger
  • className?: string - Additional class names
  • style?: CSSProperties - Additional styles

Content

Container for menu items with fade-in animation.

Props:

  • children: ReactNode - Menu items
  • className?: string - Additional class names
  • style?: CSSProperties - Additional styles

Item

Individual menu items with hover highlighting.

Props:

  • children: ReactNode - Item content
  • onSelect?: () => void - Selection callback
  • disabled?: boolean - Disable the item
  • closeOnSelect?: boolean - Close menu on select (default: true)
  • className?: string - Additional class names
  • style?: CSSProperties - Additional styles

Wrapper for nested submenu functionality.

Props:

  • id: string - Unique identifier for this submenu
  • children: ReactNode - SubMenuTrigger and SubMenuContent

The item that opens the submenu with render prop support.

Props:

  • children: ReactNode | ((isActive: boolean) => ReactNode) - Content or render prop
  • disabled?: boolean - Disable the trigger
  • className?: string - Additional class names
  • style?: CSSProperties - Additional styles

Container for submenu items that morphs from the trigger.

Props:

  • children: ReactNode - Submenu items
  • className?: string - Additional class names
  • style?: CSSProperties - Additional styles

Animation Details

The Dropdown uses spring physics for natural, fluid animations:

  • Container morph: Smooth transition of size, shape, and position
  • Lift animation: Menu lifts 75% of button height toward expansion direction
  • Content fade: Blur and opacity transition with 0.03s delay
  • Submenu scale: Parent scales to 0.96, submenu pops to 1.06
  • Reduced motion: Automatically respects user preferences

Accessibility

The dropdown implements semantic HTML and ARIA patterns:

  • Click outside or press Escape to close
  • Proper focus management on open and close
  • ARIA attributes for menu roles and states
  • Keyboard support for Enter and Space on items

Inspiration

Inspired by Bloom Menu by Josh Puckett.