System for managing nested floating elements, coordinating behavior across multiple related floating UIs, and managing hierarchical relationships between floating elements like nested menus and complex dropdowns.
Context provider for managing nested floating elements and their hierarchical relationships.
/**
* Context provider for nested floating element trees
* @param props - Tree context configuration
* @returns Tree context provider
*/
interface FloatingTreeProps {
children?: React.ReactNode;
}
declare const FloatingTree: React.FC<FloatingTreeProps>;Usage Example:
import { FloatingTree, FloatingNode, useFloating } from '@floating-ui/react';
function NestedMenus() {
return (
<FloatingTree>
<MainMenu />
</FloatingTree>
);
}
function MainMenu() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(!isOpen)}>Main Menu</button>
{isOpen && (
<FloatingNode id="main-menu">
<div>
<button>Item 1</button>
<SubMenu />
<button>Item 3</button>
</div>
</FloatingNode>
)}
</>
);
}
function SubMenu() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(!isOpen)}>Submenu →</button>
{isOpen && (
<FloatingNode id="sub-menu">
<div>
<button>Sub Item 1</button>
<button>Sub Item 2</button>
</div>
</FloatingNode>
)}
</>
);
}Individual node in a floating element tree with ID management and parent-child relationships.
/**
* Individual node in floating element tree
* @param props - Node configuration
* @returns Tree node component
*/
interface FloatingNodeProps {
children?: React.ReactNode;
id: string;
}
declare const FloatingNode: React.FC<FloatingNodeProps>;Accesses the floating element tree context and provides tree management methods.
/**
* Access floating element tree context
* @returns Tree context with events and node management
*/
function useFloatingTree(): FloatingTreeType | null;
interface FloatingTreeType {
nodesRef: React.MutableRefObject<Map<string, FloatingNodeType>>;
events: FloatingEvents;
addNode(node: FloatingNodeType): void;
removeNode(id: string): void;
}
interface FloatingNodeType {
id: string;
parentId: string | null;
context?: FloatingRootContext;
}Usage Examples:
import { useFloatingTree, FloatingTree } from '@floating-ui/react';
function TreeManager() {
const tree = useFloatingTree();
useEffect(() => {
if (tree) {
// Listen for tree events
tree.events.on('nodeopen', (data) => {
console.log('Node opened:', data.nodeId);
});
tree.events.on('nodeclose', (data) => {
console.log('Node closed:', data.nodeId);
});
}
}, [tree]);
return (
<div>
<p>Tree nodes: {tree?.nodesRef.current.size || 0}</p>
</div>
);
}
// Custom tree management
function CustomTreeNode({ id, children }: { id: string; children: React.ReactNode }) {
const tree = useFloatingTree();
useEffect(() => {
if (tree) {
const node: FloatingNodeType = {
id,
parentId: null, // Set based on context
};
tree.addNode(node);
return () => {
tree.removeNode(id);
};
}
}, [tree, id]);
return <>{children}</>;
}Gets the current node ID within the floating element tree.
/**
* Get current node ID in floating tree
* @returns Current node ID or undefined
*/
function useFloatingNodeId(): string | undefined;Gets the parent node ID for the current floating element.
/**
* Get parent node ID in floating tree
* @returns Parent node ID or null
*/
function useFloatingParentNodeId(): string | null;Usage Example:
import { useFloatingNodeId, useFloatingParentNodeId } from '@floating-ui/react';
function NodeInfo() {
const nodeId = useFloatingNodeId();
const parentId = useFloatingParentNodeId();
return (
<div>
<p>Current node: {nodeId}</p>
<p>Parent node: {parentId || 'None'}</p>
</div>
);
}Coordinates delay behavior across multiple floating elements for smooth user interactions.
/**
* Coordinates delay behavior across multiple floating elements
* @param props - Delay group configuration
* @returns Delay group context provider
*/
interface FloatingDelayGroupProps {
children?: React.ReactNode;
delay?: Delay | (() => Delay);
timeoutMs?: number;
}
declare const FloatingDelayGroup: React.FC<FloatingDelayGroupProps>;Usage Example:
import {
FloatingDelayGroup,
useDelayGroup,
useFloating,
useHover,
useInteractions
} from '@floating-ui/react';
function CoordinatedTooltips() {
return (
<FloatingDelayGroup delay={{ open: 1000, close: 200 }}>
<TooltipItem label="First tooltip">
<button>Hover me</button>
</TooltipItem>
<TooltipItem label="Second tooltip">
<button>Then me</button>
</TooltipItem>
<TooltipItem label="Third tooltip">
<button>Finally me</button>
</TooltipItem>
</FloatingDelayGroup>
);
}
function TooltipItem({
children,
label
}: {
children: React.ReactElement;
label: string;
}) {
const [isOpen, setIsOpen] = useState(false);
const { refs, floatingStyles, context } = useFloating({
open: isOpen,
onOpenChange: setIsOpen,
});
const { delay } = useDelayGroup(context, {
id: label,
});
const hover = useHover(context, {
delay,
});
const { getReferenceProps, getFloatingProps } = useInteractions([hover]);
return (
<>
{React.cloneElement(children, {
ref: refs.setReference,
...getReferenceProps(),
})}
{isOpen && (
<div
ref={refs.setFloating}
style={{
...floatingStyles,
background: 'black',
color: 'white',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
}}
{...getFloatingProps()}
>
{label}
</div>
)}
</>
);
}Experimental improved delay group with enhanced coordination features.
/**
* Experimental improved delay group coordination
* @param props - Next-generation delay group configuration
* @returns Enhanced delay group context provider
*/
interface NextFloatingDelayGroupProps {
children?: React.ReactNode;
delay?: Delay | (() => Delay);
}
declare const NextFloatingDelayGroup: React.FC<NextFloatingDelayGroupProps>;Accesses delay group context and provides coordinated delay functionality.
/**
* Access delay group functionality and coordination
* @param context - Floating UI context
* @param props - Delay group item configuration
* @returns Delay group state and delay values
*/
function useDelayGroup(
context: FloatingRootContext,
props: UseDelayGroupProps
): {
delay: Delay;
isInstantPhase: boolean;
currentId: React.MutableRefObject<string | null>;
};
interface UseDelayGroupProps {
id: string;
}Legacy hook for accessing delay group context.
/**
* Access delay group context (deprecated - use useDelayGroup)
* @returns Delay group context or null
*/
function useDelayGroupContext(): {
delay: Delay;
timeoutMs: number;
} | null;Experimental hook for enhanced delay group coordination.
/**
* Experimental enhanced delay group hook
* @param context - Floating UI context
* @param props - Next delay group configuration
* @returns Enhanced delay group state
*/
function useNextDelayGroup(
context: FloatingRootContext,
props: UseNextDelayGroupProps
): {
delay: Delay;
};
interface UseNextDelayGroupProps {
id: string;
}<FloatingTree>
<FloatingNode id="main">
<MainMenu>
<FloatingNode id="sub1">
<SubMenu />
</FloatingNode>
</MainMenu>
</FloatingNode>
</FloatingTree><FloatingTree>
<ContextMenu>
<FloatingNode id="context">
<MenuItem>
<FloatingNode id="submenu">
<SubContextMenu />
</FloatingNode>
</MenuItem>
</FloatingNode>
</ContextMenu>
</FloatingTree><FloatingDelayGroup delay={{ open: 500, close: 100 }}>
<TooltipButton id="btn1" />
<TooltipButton id="btn2" />
<TooltipButton id="btn3" />
</FloatingDelayGroup>The tree context provides event coordination:
Delay groups coordinate timing:
function MenuWithSubmenu() {
const nodeId = useFloatingNodeId();
const parentId = useFloatingParentNodeId();
const tree = useFloatingTree();
useEffect(() => {
// Custom logic based on tree position
if (tree && nodeId) {
const node = tree.nodesRef.current.get(nodeId);
if (node?.parentId) {
// Handle parent-child coordination
}
}
}, [tree, nodeId]);
return (
<FloatingNode id="submenu">
{/* Submenu content */}
</FloatingNode>
);
}function TreeStateManager() {
const tree = useFloatingTree();
const [openNodes, setOpenNodes] = useState<Set<string>>(new Set());
useEffect(() => {
if (tree) {
tree.events.on('nodeopen', ({ nodeId }) => {
setOpenNodes(prev => new Set(prev).add(nodeId));
});
tree.events.on('nodeclose', ({ nodeId }) => {
setOpenNodes(prev => {
const next = new Set(prev);
next.delete(nodeId);
return next;
});
});
}
}, [tree]);
return (
<div>
<p>Open nodes: {Array.from(openNodes).join(', ')}</p>
</div>
);
}