Skip to content

Instantly share code, notes, and snippets.

@romgrk
Created November 25, 2025 19:07
Show Gist options
  • Select an option

  • Save romgrk/28b59562a533016c766628e3a717e79e to your computer and use it in GitHub Desktop.

Select an option

Save romgrk/28b59562a533016c766628e3a717e79e to your computer and use it in GitHub Desktop.
'use client';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Menu } from '@base-ui-components/react/menu';
import { Tooltip } from '@base-ui-components/react/tooltip';
import menuDemoStyles from './menu-styles.module.css';
import tooltipDemoStyles from './tooltip-styles.module.css';
import styles from './perf.module.css';
type RowData = {
label: string;
index: number;
};
const rowCount = 500;
const menuItemCount = 50;
const rows = Array.from({ length: rowCount }).map((_, i) => ({
label: `Row ${i + 1}`,
index: i + 1,
}));
const menuItems = Array.from({ length: menuItemCount }).map((_, i) => ({
label: `Menu Item ${i + 1}`,
index: i + 1,
}));
const rowMenuHandle = Menu.createHandle<RowData>();
const genericTooltipHandle = Tooltip.createHandle<string>();
let setShowBenchmark = (_: boolean) => {};
export default function PerfExperiment() {
const runBenchmark = (iterations = 10, warmupIterations = 5) => {
const results = [] as number[];
for (let i = 0; i < warmupIterations + iterations; i++) {
ReactDOM.flushSync(() => {
setShowBenchmark(false);
});
const start = performance.now();
ReactDOM.flushSync(() => {
setShowBenchmark(true);
});
if (i < warmupIterations) {
continue;
}
const end = performance.now();
const elapsed = end - start;
results.push(Math.round(elapsed * 10) / 10);
}
console.log(results);
console.log(
'Average:',
Math.round((results.reduce((a, b) => a + b, 0) / results.length) * 10) / 10,
);
console.log(
'Std Dev:',
(() => {
const avg = results.reduce((a, b) => a + b, 0) / results.length;
const squareDiffs = results.map((value) => {
const diff = value - avg;
return diff * diff;
});
const avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / squareDiffs.length;
return +Math.sqrt(avgSquareDiff).toFixed(2);
})(),
);
};
return (
<div className={styles.container}>
<div>
<button onClick={() => setShowBenchmark((prev) => !prev)}>Toggle</button>
<button onClick={() => runBenchmark(10, 5)} style={{ marginLeft: 8 }}>
Run 10
</button>
<button onClick={() => runBenchmark(20, 5)} style={{ marginLeft: 8 }}>
Run 20
</button>
<button onClick={() => runBenchmark(50, 5)} style={{ marginLeft: 8 }}>
Run 50
</button>
</div>
<Container />
</div>
);
}
function Container() {
const [showBenchmark, setShowBenchmarkLocal] = React.useState(false);
setShowBenchmark = setShowBenchmarkLocal;
if (!showBenchmark) {
return null;
}
return <Benchmark />;
}
function Benchmark() {
return (
<div>
<div className={styles.rows}>
{rows.map((row) => (
<div key={row.index} className={styles.row}>
<span className={styles.label}>{row.label}</span>
{
undefined
// <Menu.Trigger handle={rowMenuHandle} payload={row} />
}
{
// undefined
<Tooltip.Trigger
handle={genericTooltipHandle}
render={(props) => (
<Menu.Trigger {...props} handle={rowMenuHandle} payload={row} />
)}
closeDelay={60 * 60 * 1000}
className={menuDemoStyles.Trigger}
payload={`Actions menu for ${row.label}`}
data-id={row.index}
>
•••
</Tooltip.Trigger>
}
</div>
))}
</div>
<RowMenu />
<GenericTooltip />
</div>
);
}
function RowMenu() {
return (
<Menu.Root handle={rowMenuHandle}>
{({ payload: rowData }) => (
<Menu.Portal>
<Menu.Positioner sideOffset={8} className={menuDemoStyles.Positioner}>
<Menu.Popup className={menuDemoStyles.Popup}>
<Menu.Arrow className={menuDemoStyles.Arrow}>
<ArrowSvg />
</Menu.Arrow>
{rowData && (
<React.Fragment>
{menuItems.map((item) => (
<Menu.Item
key={item.index}
onClick={() =>
console.log(`Clicked ${item.label} for ${rowData.label}`)
}
className={menuDemoStyles.Item}
>
{item.label} for {rowData.label}
</Menu.Item>
))}
</React.Fragment>
)}
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
)}
</Menu.Root>
);
}
function GenericTooltip() {
return (
<Tooltip.Root handle={genericTooltipHandle}>
{({ payload: content }) =>
content ? (
<Tooltip.Portal>
<Tooltip.Positioner sideOffset={10}>
<Tooltip.Popup className={tooltipDemoStyles.Popup}>
<Tooltip.Arrow className={tooltipDemoStyles.Arrow}>
<ArrowSvg />
</Tooltip.Arrow>
{content}
</Tooltip.Popup>
</Tooltip.Positioner>
</Tooltip.Portal>
) : null
}
</Tooltip.Root>
);
}
function ArrowSvg(props: React.ComponentProps<'svg'>) {
return (
<svg width="20" height="10" viewBox="0 0 20 10" fill="none" {...props}>
<path
d="M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z"
className={menuDemoStyles.ArrowFill}
/>
<path
d="M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z"
className={menuDemoStyles.ArrowOuterStroke}
/>
<path
d="M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z"
className={menuDemoStyles.ArrowInnerStroke}
/>
</svg>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment