Low-level hooks and context providers for custom menu implementations, accessing menu state, and building advanced menu interactions. These provide the foundation for all menu functionality.
Main hook that provides complete menu state management, focus handling, and positioning logic.
/**
* Main menu state and logic management hook
* @param props - Menu configuration options
* @returns Complete menu state and control functions
*/
function useMenu(props?: UseMenuProps): UseMenuReturn;
interface UseMenuProps extends Omit<UsePopperProps, "enabled">, UseDisclosureProps {
/** Ref to element that should receive focus when menu opens */
initialFocusRef?: React.RefObject<{ focus(): void }>;
/** Whether menu closes when item is clicked */
closeOnSelect?: boolean;
/** Whether menu closes when clicking outside */
closeOnBlur?: boolean;
/** Whether first item is auto-selected when menu opens */
autoSelect?: boolean;
/** Whether menu rendering is deferred until open */
isLazy?: boolean;
/** Lazy rendering behavior - "unmount" or "keepMounted" */
lazyBehavior?: LazyMode;
/** Text direction for proper placement positioning */
direction?: "ltr" | "rtl";
/** Whether menu positions immediately on mount */
computePositionOnMount?: boolean;
}
interface UseMenuReturn {
/** Whether menu is currently open */
isOpen: boolean;
/** Function to open menu */
onOpen: () => void;
/** Function to close menu */
onClose: () => void;
/** Function to toggle menu state */
onToggle: () => void;
/** Ref to menu button element */
buttonRef: React.RefObject<HTMLButtonElement>;
/** Ref to menu list element */
menuRef: React.RefObject<HTMLDivElement>;
/** Currently focused menu item index */
focusedIndex: number;
/** Function to set focused item index */
setFocusedIndex: (index: number) => void;
/** Descendant tracking context */
descendants: DescendantsManager<HTMLElement>;
/** Popper positioning utilities */
popper: PopperReturn;
/** Unique button ID for accessibility */
buttonId: string;
/** Unique menu ID for accessibility */
menuId: string;
/** Menu orientation (always "vertical") */
orientation: "vertical";
/** Close on select setting */
closeOnSelect: boolean;
/** Close on blur setting */
closeOnBlur: boolean;
/** Auto select setting */
autoSelect: boolean;
/** Lazy rendering setting */
isLazy: boolean;
/** Lazy behavior mode */
lazyBehavior: LazyMode;
/** Initial focus ref */
initialFocusRef?: React.RefObject<{ focus(): void }>;
/** Animation frame ID for focus management */
rafId: React.MutableRefObject<number | null>;
/** Force update function for positioning */
forceUpdate?: () => void;
/** Open menu and focus first item */
openAndFocusFirstItem: () => void;
/** Open menu and focus last item */
openAndFocusLastItem: () => void;
/** Open menu and focus menu container */
openAndFocusMenu: () => void;
/** Transition end handler */
onTransitionEnd: () => void;
/** Animation state */
unstable__animationState: AnimationState;
}Usage Examples:
import { useMenu } from "@chakra-ui/menu";
// Basic custom menu
function CustomMenu() {
const menu = useMenu();
return (
<div>
<button
ref={menu.buttonRef}
onClick={menu.onToggle}
aria-expanded={menu.isOpen}
aria-haspopup="menu"
aria-controls={menu.menuId}
>
Custom Menu
</button>
{menu.isOpen && (
<div
ref={menu.menuRef}
id={menu.menuId}
role="menu"
tabIndex={-1}
>
<button role="menuitem">Option 1</button>
<button role="menuitem">Option 2</button>
</div>
)}
</div>
);
}
// Controlled menu
function ControlledCustomMenu() {
const [isOpen, setIsOpen] = useState(false);
const menu = useMenu({
isOpen,
onClose: () => setIsOpen(false),
onOpen: () => setIsOpen(true),
closeOnSelect: false,
autoSelect: false
});
return (
<div>
<button
ref={menu.buttonRef}
onClick={() => setIsOpen(!isOpen)}
>
Controlled Menu ({isOpen ? 'Open' : 'Closed'})
</button>
{menu.isOpen && (
<div ref={menu.menuRef} role="menu">
<p>Current focus: {menu.focusedIndex}</p>
<button
role="menuitem"
onClick={() => menu.setFocusedIndex(0)}
>
Focus me
</button>
</div>
)}
</div>
);
}Hook that provides menu button behavior, event handlers, and accessibility attributes.
/**
* Menu button behavior and properties hook
* @param props - Button configuration options
* @param externalRef - External ref to merge with button ref
* @returns Button props with event handlers and ARIA attributes
*/
function useMenuButton(
props?: UseMenuButtonProps,
externalRef?: React.Ref<any>
): ButtonProps;
interface UseMenuButtonProps extends Omit<React.HTMLAttributes<Element>, "color"> {
/** Additional button properties */
[key: string]: any;
}
interface ButtonProps extends UseMenuButtonProps {
/** Merged ref for button element */
ref: React.RefObject<any>;
/** Unique button ID */
id: string;
/** Active state data attribute */
"data-active": string | undefined;
/** ARIA expanded state */
"aria-expanded": boolean;
/** ARIA haspopup attribute */
"aria-haspopup": "menu";
/** ARIA controls attribute */
"aria-controls": string;
/** Click event handler */
onClick: (event: React.MouseEvent) => void;
/** Keydown event handler */
onKeyDown: (event: React.KeyboardEvent) => void;
}Usage Examples:
import { useMenuButton, useMenuContext } from "@chakra-ui/menu";
// Custom menu button
function CustomMenuButton(props) {
const buttonProps = useMenuButton(props);
return (
<button
{...buttonProps}
style={{
padding: '8px 16px',
border: '1px solid #ccc',
borderRadius: '4px',
backgroundColor: buttonProps['data-active'] ? '#e2e8f0' : 'white'
}}
>
{props.children}
</button>
);
}
// Button with custom handling
function CustomButton() {
const menu = useMenuContext();
const buttonProps = useMenuButton({
onKeyDown: (e) => {
console.log('Button key:', e.key);
// Custom key handling before default behavior
},
onClick: (e) => {
console.log('Button clicked');
// Custom click handling before default behavior
}
});
return <button {...buttonProps}>Advanced Button</button>;
}Hook that provides menu list behavior, keyboard navigation, and accessibility attributes.
/**
* Menu list behavior and properties hook
* @param props - List configuration options
* @param ref - External ref to merge with list ref
* @returns List props with keyboard handlers and ARIA attributes
*/
function useMenuList(
props?: UseMenuListProps,
ref?: React.Ref<any>
): ListProps;
interface UseMenuListProps extends Omit<React.HTMLAttributes<Element>, "color"> {
/** Additional list properties */
[key: string]: any;
}
interface ListProps extends UseMenuListProps {
/** Merged ref for list element */
ref: React.RefObject<any>;
/** List children (may be null for lazy rendering) */
children: React.ReactNode | null;
/** Tab index for focus management */
tabIndex: number;
/** ARIA role */
role: "menu";
/** Unique menu ID */
id: string;
/** Inline styles for positioning */
style: React.CSSProperties;
/** ARIA orientation */
"aria-orientation": "vertical";
/** Keydown event handler */
onKeyDown: (event: React.KeyboardEvent) => void;
}Usage Examples:
import { useMenuList, useMenuContext } from "@chakra-ui/menu";
// Custom menu list
function CustomMenuList(props) {
const listProps = useMenuList(props);
return (
<div
{...listProps}
style={{
...listProps.style,
backgroundColor: 'white',
border: '1px solid #ccc',
borderRadius: '6px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
padding: '4px',
minWidth: '200px'
}}
/>
);
}
// List with custom keyboard handling
function CustomListWithKeys() {
const menu = useMenuContext();
const listProps = useMenuList({
onKeyDown: (e) => {
if (e.key === 'Escape') {
console.log('Custom escape handling');
}
// Default behavior will still apply
}
});
return (
<div {...listProps}>
<p>Focus index: {menu.focusedIndex}</p>
{props.children}
</div>
);
}Hook that provides individual menu item behavior, focus management, and click handling.
/**
* Menu item behavior and properties hook
* @param props - Item configuration options
* @param externalRef - External ref to merge with item ref
* @returns Item props with event handlers and focus management
*/
function useMenuItem(
props?: UseMenuItemProps,
externalRef?: React.Ref<any>
): ItemProps;
interface UseMenuItemProps extends Omit<React.HTMLAttributes<Element>, "color" | "disabled"> {
/** Whether the menu item is disabled */
isDisabled?: boolean;
/** Whether disabled items remain keyboard-focusable */
isFocusable?: boolean;
/** Override parent menu's closeOnSelect prop */
closeOnSelect?: boolean;
/** Button type attribute */
type?: React.ButtonHTMLAttributes<HTMLButtonElement>["type"];
}
interface ItemProps extends UseMenuItemProps {
/** Merged ref for item element */
ref: React.RefObject<any>;
/** Unique item ID */
id: string;
/** ARIA role */
role: "menuitem";
/** Tab index for focus management */
tabIndex: number;
/** Click event handler */
onClick: (event: React.MouseEvent) => void;
/** Focus event handler */
onFocus: (event: React.FocusEvent) => void;
/** Mouse enter event handler */
onMouseEnter: (event: React.MouseEvent) => void;
/** Mouse move event handler */
onMouseMove: (event: React.MouseEvent) => void;
/** Mouse leave event handler */
onMouseLeave: (event: React.MouseEvent) => void;
}Usage Examples:
import { useMenuItem } from "@chakra-ui/menu";
// Custom menu item
function CustomMenuItem(props) {
const itemProps = useMenuItem(props);
return (
<button
{...itemProps}
style={{
width: '100%',
padding: '8px 12px',
textAlign: 'left',
border: 'none',
backgroundColor: itemProps.tabIndex === 0 ? '#e2e8f0' : 'transparent',
cursor: props.isDisabled ? 'not-allowed' : 'pointer',
opacity: props.isDisabled ? 0.6 : 1
}}
>
{props.children}
</button>
);
}
// Item with custom click behavior
function CustomClickItem({ children, onCustomClick }) {
const itemProps = useMenuItem({
onClick: (e) => {
onCustomClick?.(e);
// Default close behavior will still apply
}
});
return (
<button {...itemProps}>
{children}
</button>
);
}React context provider that shares menu state and functions with all child components.
/**
* React context provider for menu state and functions
* @param value - Menu context value to provide to children
* @param children - Child components that will receive menu context
*/
const MenuProvider: React.Provider<MenuContextValue>;
interface MenuContextValue extends Omit<UseMenuReturn, "descendants"> {
/** All menu state and functions except descendants */
[key: string]: any;
}Usage Examples:
import { MenuProvider, useMenu } from "@chakra-ui/menu";
function CustomMenuContainer({ children }) {
const menu = useMenu();
return (
<MenuProvider value={menu}>
{children}
</MenuProvider>
);
}React context provider that manages the descendant tracking system for menu items.
/**
* React context provider for descendant tracking
* @param value - Descendants manager instance
* @param children - Child components that will receive descendant context
*/
const MenuDescendantsProvider: React.Provider<DescendantsManager<HTMLElement>>;
interface DescendantsManager<T extends HTMLElement> {
/** Array of all registered descendants */
values(): Descendant<T>[];
/** Get descendant by index */
item(index: number): Descendant<T> | undefined;
/** Get index of element */
indexOf(element: T): number;
/** Navigation utilities for focus management */
firstEnabled(): Descendant<T> | undefined;
lastEnabled(): Descendant<T> | undefined;
nextEnabled(index: number): Descendant<T> | undefined;
prevEnabled(index: number): Descendant<T> | undefined;
}Usage Examples:
import { MenuDescendantsProvider, useMenuDescendants } from "@chakra-ui/menu";
function CustomMenuWithDescendants({ children }) {
const descendants = useMenuDescendants();
return (
<MenuDescendantsProvider value={descendants}>
{children}
</MenuDescendantsProvider>
);
}Hook to access the current menu context containing all menu state and functions.
/**
* Access current menu context
* @returns Menu context with state and control functions
*/
function useMenuContext(): MenuContextValue;
interface MenuContextValue extends Omit<UseMenuReturn, "descendants"> {
/** All menu state and functions except descendants */
[key: string]: any;
}Usage Examples:
import { useMenuContext } from "@chakra-ui/menu";
// Component that shows menu state
function MenuStatus() {
const menu = useMenuContext();
return (
<div>
<p>Menu is {menu.isOpen ? 'open' : 'closed'}</p>
<p>Focused item: {menu.focusedIndex}</p>
<button onClick={menu.onClose}>Force Close</button>
</div>
);
}
// Custom menu item that accesses context
function ContextMenuItem({ children }) {
const menu = useMenuContext();
return (
<button
onClick={() => {
console.log(`Menu has ${menu.descendants.values().length} items`);
menu.onClose();
}}
>
{children}
</button>
);
}Hook to access the current menu theme styles for consistent styling across custom components.
/**
* Access menu theme styles
* @returns Style object with theme-based styles for menu components
*/
function useMenuStyles(): Record<string, SystemStyleObject>;
interface SystemStyleObject {
/** CSS properties and values */
[property: string]: any;
}Usage Examples:
import { useMenuStyles } from "@chakra-ui/menu";
// Custom component using theme styles
function ThemedMenuItem({ children }) {
const styles = useMenuStyles();
return (
<button
style={{
// Apply theme styles
...styles.item,
// Add custom overrides
fontWeight: 'bold'
}}
>
{children}
</button>
);
}
// Component that shows available styles
function StyleInspector() {
const styles = useMenuStyles();
return (
<div>
<h3>Available Styles:</h3>
<ul>
{Object.keys(styles).map(key => (
<li key={key}>{key}</li>
))}
</ul>
</div>
);
}Hook for managing the collection of menu item descendants for focus navigation.
/**
* Manage collection of menu item descendants
* @returns Descendant manager with navigation utilities
*/
function useMenuDescendants(): DescendantsManager<HTMLElement>;
interface DescendantsManager<T extends HTMLElement> {
/** Array of all registered descendants */
values(): Descendant<T>[];
/** Get descendant by index */
item(index: number): Descendant<T> | undefined;
/** Get index of element */
indexOf(element: T): number;
/** Get first enabled descendant */
firstEnabled(): Descendant<T> | undefined;
/** Get last enabled descendant */
lastEnabled(): Descendant<T> | undefined;
/** Get next enabled descendant after index */
nextEnabled(index: number): Descendant<T> | undefined;
/** Get previous enabled descendant before index */
prevEnabled(index: number): Descendant<T> | undefined;
}Hook to access the descendant tracking context for menu items.
/**
* Access descendant tracking context
* @returns Descendant manager from context
*/
function useMenuDescendantsContext(): DescendantsManager<HTMLElement>;Usage Examples:
import { useMenuDescendantsContext } from "@chakra-ui/menu";
function MenuItemCounter() {
const descendants = useMenuDescendantsContext();
return (
<div>
Total menu items: {descendants.values().length}
</div>
);
}
function FocusNavigator() {
const descendants = useMenuDescendantsContext();
const goToFirst = () => {
const first = descendants.firstEnabled();
if (first) first.node.focus();
};
const goToLast = () => {
const last = descendants.lastEnabled();
if (last) last.node.focus();
};
return (
<div>
<button onClick={goToFirst}>Focus First</button>
<button onClick={goToLast}>Focus Last</button>
</div>
);
}Hook for registering individual menu items as descendants for focus management.
/**
* Register menu item as descendant
* @param options - Registration options
* @returns Registration utilities
*/
function useMenuDescendant(options?: DescendantOptions): DescendantReturn;
interface DescendantOptions {
/** Whether this descendant is disabled */
disabled?: boolean;
}
interface DescendantReturn {
/** Index of this descendant */
index: number;
/** Function to register the descendant element */
register: (element: HTMLElement | null) => void;
}Hook for menu option items with selection state management.
/**
* Menu option item with selection state
* @param props - Option configuration
* @param ref - External ref
* @returns Option props with selection state
*/
function useMenuOption(
props?: UseMenuOptionProps,
ref?: React.Ref<any>
): OptionProps;
interface UseMenuOptionProps extends Omit<UseMenuItemProps, "type">, UseMenuOptionOptions {
/** Selection type */
type?: "radio" | "checkbox";
/** Whether option is checked */
isChecked?: boolean;
/** Option value */
value?: string;
}Hook for managing groups of selectable menu options.
/**
* Manage group of selectable menu options
* @param props - Group configuration
* @returns Group props with selection management
*/
function useMenuOptionGroup(props?: UseMenuOptionGroupProps): GroupProps;
interface UseMenuOptionGroupProps {
/** Current value(s) */
value?: string | string[];
/** Default value(s) */
defaultValue?: string | string[];
/** Selection type */
type?: "radio" | "checkbox";
/** Change handler */
onChange?: (value: string | string[]) => void;
/** Group children */
children?: React.ReactNode;
}Hook for accessing menu positioning utilities from Popper.js integration. Provides positioning props for the menu list wrapper element.
/**
* Access menu positioning utilities from Popper.js
* @param props - Additional props to merge with positioning props
* @returns Popper positioning props with visibility control
*/
function useMenuPositioner(props?: any): PopperProps;
interface PopperProps {
/** Positioning styles including visibility control */
style: React.CSSProperties & {
visibility: "visible" | "hidden";
};
/** Popper positioning data attributes */
"data-popper-placement"?: string;
"data-popper-reference-hidden"?: string;
"data-popper-escaped"?: string;
/** Additional popper props merged from params */
[key: string]: any;
}Usage Examples:
import { useMenuPositioner, useMenuContext } from "@chakra-ui/menu";
// Custom positioned menu wrapper
function CustomMenuWrapper(props) {
const positionerProps = useMenuPositioner({
padding: 8,
className: "custom-menu-positioner"
});
return (
<div
{...positionerProps}
style={{
...positionerProps.style,
zIndex: 9999,
borderRadius: '8px'
}}
>
{props.children}
</div>
);
}
// Access positioning data
function PositionInfo() {
const positionerProps = useMenuPositioner();
return (
<div {...positionerProps}>
<p>Placement: {positionerProps['data-popper-placement']}</p>
<p>Visible: {positionerProps.style.visibility}</p>
</div>
);
}Simple hook to access just the menu open/close state.
/**
* Access menu open/close state
* @returns Menu state object
*/
function useMenuState(): MenuState;
interface MenuState {
/** Whether menu is open */
isOpen: boolean;
/** Function to close menu */
onClose: () => void;
}