Basic Example
Simple list with default overflow element
<OverflowList
items={fruits}
renderItem={(item, index) => (
<span key={index} className="fruit-item">
{item}
</span>
)}
style={{ gap: "8px" }}
/>Children Pattern
Using children instead of items array
<OverflowList>
<button>Action 1</button>
<button>Action 2</button>
...
</OverflowList>Multi-row Example
Allow up to 2 rows before overflow
<OverflowList
items={fruits.concat(tags).concat(menuItems)}
renderItem={(item) => <span className="multi-item">{item}</span>}
maxRows={2}
style={{ gap: "4px" }}
/>Custom Overflow Element
You would generally want to provide your own overflow element
Simple Overflow Function
<OverflowList
items={tags}
renderItem={(tag) => <span className="tag">#{tag}</span>}
renderOverflow={(items) => <div>{items.length} items are hidden</div>}
maxRows={1}
style={{ gap: "6px" }}
/>ForwardRef Component
When you provide a custom overflow component, it should be a forwardRef component to ensure proper ref forwarding for measurements.
// Custom overflow component with forwardRef
const CustomOverflowElement = React.forwardRef<HTMLDivElement, { items: string[] }>(
({ items }, ref) => (
<div ref={ref} className="custom-overflow">
<span>+{items.length} more items</span>
</div>
)
);
<OverflowList
items={tags}
renderItem={(tag) => <span className="tag">#{tag}</span>}
renderOverflow={(items) => <CustomOverflowElement items={items} />}
maxRows={1}
style={{ gap: "6px" }}
/>Radix UI Dropdown Menu
Advanced example using Radix UI's dropdown menu with proper accessibility and keyboard navigation.
<OverflowList
items={tags}
renderItem={(tag) => <span className="tag">#{tag}</span>}
renderOverflow={(items) => <RadixOverflowMenu items={items} />}
maxRows={1}
style={{ gap: "6px" }}
/>
// Implementation:
import React from "react";
import { DropdownMenu } from "@radix-ui/themes";
const RadixOverflowMenu = React.forwardRef<HTMLButtonElement, { items: string[] }>(({ items }, ref) => (
<DropdownMenu.Root modal={false}>
<DropdownMenu.Trigger>
<button ref={ref} className="demo-button demo-button--primary">
+{items.length} more
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{items.map((item, index) => (
<DropdownMenu.Item key={index}>#{item}</DropdownMenu.Item>
))}
</DropdownMenu.Content>
</DropdownMenu.Root>
));
export { RadixOverflowMenu };
Virtualized Radix UI Dropdown (10,000+ Items)
Advanced example combining Radix UI with TanStack Virtual for handling thousands of items efficiently. The maxVisibleItems prop (default: 100) ensures that only the first N items are rendered in the main list, while everything beyond that threshold gets moved to the overflow dropdown.
maxVisibleItems prop (default: 100) ensures only the first 100 items are rendered in the main list, even on the initial render. The remaining 9,900 items are efficiently handled by the virtualized dropdown, which only renders the visible portion of the scrollable list.Performance Tip: Lower
maxVisibleItems values provide better performance (fewer DOM nodes on initial render), while higher values allow more items to be visible before overflowing but with reduced performance. The default of 100 provides a good balance between performance and usability.Custom Host Element
Using the 'as' prop to render as different HTML elements
<OverflowList as="nav" style={{ gap: "8px" }}>
<a href="#home">Home</a>
<a href="#about">About</a>
<a href="#contact">Contact</a>
</OverflowList>Extending OverflowList
This is an example implementation showing how to wrap OverflowList with Radix UI dropdown and virtualization. In real-world applications, it's expected that you'll wrap OverflowList with your own components tailored to your specific needs, design system, etc.
import { RadixVirtualizedOverflowList } from "../components/RadixVirtualizedOverflowList";
// Small dataset - uses simple dropdown
<RadixVirtualizedOverflowList
items={tags}
renderItem={(tag) => <span className="tag">#{tag}</span>}
style={{ gap: "6px" }}
/>
// Large dataset - automatically uses virtualization
<RadixVirtualizedOverflowList
items={Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`)}
renderItem={(item) => <span className="tag">#{item}</span>}
virtualizationThreshold={50}
enableSearch={true}
maxRows={1}
style={{ gap: "6px" }}
/>Small Dataset (Simple Dropdown)
Large Dataset (Virtualized Dropdown with Search)
- Automatic virtualization: Switches to virtualized dropdown when item count exceeds threshold
- Search functionality: Built-in search/filter for large datasets
- Radix UI integration: Full accessibility and keyboard navigation support
- Customizable: Configurable thresholds, styling, and behavior
- Performance optimized: Efficient rendering for thousands of items
Note: This is just an example implementation. In real-world applications, it's expected that you'll wrap OverflowList with your own components tailored to your specific needs and design system.
Source: View implementation on GitHub
Flush Immediately Example
Control how updates are applied when the container resizes.flushImmediately=false(default: true)
<OverflowList
items={fruits.concat(tags)}
renderItem={(item) => <span className="multi-item">{item}</span>}
flushImmediately={false}
style={{ gap: "4px" }}
/>Trade-offs:
- flushImmediately=true: Updates are applied immediately using flushSync, avoiding flickering but may impact performance
- flushImmediately=false: Updates are applied in the requestAnimationFrame callback, avoiding forced reflow and improving performance but may cause slight flickering
- Default behavior: flushImmediately is true by default to prioritize smooth visual experience
One Item Wider Than Container
What happens when a single item is wider than the available container space? The component gracefully handles this by showing only the overflow element.
<OverflowList
items={["This is a very long item that exceeds container width"]}
renderItem={(item) => (
<span style={{ whiteSpace: "nowrap", padding: "8px 16px", backgroundColor: "#e3f2fd" }}>
{item}
</span>
)}
style={{ gap: "8px", width: "200px" }}
/>Reverse Order Example
Compare normal overflow (shrinks from end) vs reverse overflow (shrinks from start)
// Normal overflow - shrinks from end
<OverflowList
items={tags}
renderItem={(item, index) => (
<span key={index} className="tag-item">
{item}
</span>
)}
style={{ gap: "8px" }}
/>
// Reverse overflow - shrinks from start
<OverflowList
items={[...tags].reverse()}
renderItem={(item, index) => (
<span key={index} className="tag-item">
{item}
</span>
)}
style={{
gap: "8px",
flexDirection: "row-reverse",
justifyContent: "flex-end"
}}
/>