Migration of Menu from v5.x to 6.x
February 4, 2026 · View on GitHub
Breaking change: Changed prop types
The Menu component introduces two changes:
- placement no longer accepts
"auto-start"and"auto-end" - offset prop shape changed from a tuple [number, number] to OffsetOptions (number or object)
Summary of changes
| Prop | Old Type | New Type | Default (Old → New) | Status |
|---|---|---|---|---|
children | React.ReactNode | React.ReactNode | n/a | Unchanged |
isFullWidth | boolean | boolean | false → false | Unchanged |
isOpen | boolean | boolean | false → false | Unchanged |
defaultIsOpen | boolean | boolean | false → false | Unchanged |
onClose | () => void | () => void | n/a | Unchanged |
onOpen | () => void | () => void | n/a | Unchanged |
placement | "auto", "left", "right", "top", "bottom", "auto-start", "auto-end", "top-start", "top-end", "bottom-start", "bottom-end", "right-start", "right-end", "left-start" | "auto", "top", "right", "bottom", "left", "bottom-start", "bottom-end", "left-start", "left-end","right-start", "right-end", "top-start", "top-end" | 'auto' OR bottom-start → bottom-start | Changed "auto-start" and "auto-end" have been removed, as auto-adjustment for "top", "right", "bottom", "left" is already applied |
isAutoalignmentEnabled | boolean | boolean | true → true | Unchanged |
usePortal | boolean | boolean | true → true | Unchanged |
useTypeahead | boolean | boolean | false | New |
closeOnBlur | boolean | boolean | true → true | Unchanged |
closeOnEsc | boolean | boolean | true → true | Unchanged |
autoFocus | boolean | boolean | true → true | Unchanged |
id | string | string | undefined → undefined | Unchanged |
offset | [number, number] | number OR { mainAxis?: number; crossAxis?: number; alignmentAxis?: number }` | [1,4] → 0 | Changed Requires migration (see mapping) |
renderOnlyWhenOpen | boolean | boolean | true → true | Unchanged |
Property placement migration
OLD: auto-start or auto-end are no longer supported. Auto-adjustment for "top", "right", "bottom", "left" is already applied, so either can be used.
placement = 'auto-start';
NEW:
placement = 'left-start';
Use when you prefer automatic best-fit without manually managing flipping logic.
Property offset migration
OLD: [x, y] where x = horizontal shift, y = vertical gap (for top/bottom placements).
NEW: offset can be:
number→ shorthand for{ mainAxis: number }- object:
{ mainAxis?, crossAxis?, alignmentAxis? }
Axis mapping depends on the primary side:
- top / bottom: mainAxis = vertical gap, crossAxis = horizontal shift
- left / right: mainAxis = horizontal gap, crossAxis = vertical shift
Mapping Table
| Primary Placement | Old [x, y] | New Object |
|---|---|---|
| top / bottom | [x, y] | { mainAxis: y, crossAxis: x } |
| left / right | [x, y] | { mainAxis: x, crossAxis: y } |
Default change:
- Old default
[1,4] - New default
0Approximate previous default (bottom-start):{ mainAxis: 4, crossAxis: 1 }(or justoffset={4}if the small cross shift was negligible).
Examples
Old:
<Menu placement="bottom-start" offset={[0, 8]} />
New:
<Menu placement="bottom-start" offset={{ mainAxis: 8, crossAxis: 0 }} />
Old:
<Menu placement="left" offset={[8, 2]} />
New:
<Menu placement="left" offset={{ mainAxis: 8, crossAxis: 2 }} />
Shorthand:
<Menu offset={8} /> // { mainAxis: 8 }
Advanced alignment tweak:
<Menu placement="bottom-end" offset={{ mainAxis: 8, alignmentAxis: -4 }} />
Optional Helper
function migrateOffset(oldOffset: [number, number], placement: string) {
const [x, y] = oldOffset;
const primary = placement.split('-')[0];
if (primary === 'top' || primary === 'bottom') {
return { mainAxis: y, crossAxis: x };
}
return { mainAxis: x, crossAxis: y };
}
New Property Typeahead
The useTypeahead prop enables a quick navigation feature within a Menu. If a user opens the menu and then starts typing, the focus will jump to the first entry with the typed letter.
Important: useTypeahead will catch and intervene onKeyDown events. So it can not be used with TextInput inside the MenuList.
Example
<Menu useTypeahead>
<Menu.Trigger>
<Button>Open</Button>
</Menu.Trigger>
<Menu.List>
<Menu.Item>Apple</Menu.Item>
<Menu.Submenu>
<Menu.SubmenuTrigger>Banana</Menu.SubmenuTrigger>
<Menu.List>
<Menu.Item>Cantaloupe</Menu.Item>
<Menu.Item>Dates</Menu.Item>
</Menu.List>
</Menu.Submenu>
</Menu.List>
</Menu>
Deprecated: Menu.Submenu
The compound component Menu.Submenu has been deprecated in v6.
What changed
Previously you could nest menus using:
<Menu>
<Menu.Trigger>
<Button>Actions</Button>
</Menu.Trigger>
<Menu.List>
<Menu.Item>Item 1</Menu.Item>
<Menu.Submenu>
<Menu.SubmenuTrigger>More actions</Menu.SubmenuTrigger>
<Menu.List>
<Menu.Item>Nested A</Menu.Item>
<Menu.Item>Nested B</Menu.Item>
</Menu.List>
</Menu.Submenu>
</Menu.List>
</Menu>
In v6 you should compose nested menus directly with Menu elements. The nested Menu automatically detects its parent and switches to submenu (fly-out) behavior (placement defaults to right-start).
Migration
- Replace
<Menu.Submenu>with<Menu>. - Keep the nested
<Menu.List>and its<Menu.Item>children unchanged.
Updated example:
<Menu>
<Menu.Trigger>
<Button>Actions</Button>
</Menu.Trigger>
<Menu.List>
<Menu.Item>Item 1</Menu.Item>
<Menu>
<Menu.SubmenuTrigger>More actions</Menu.SubmenuTrigger>
<Menu.List>
<Menu.Item>Nested A</Menu.Item>
<Menu.Item>Nested B</Menu.Item>
</Menu.List>
</Menu>
</Menu.List>
</Menu>
Why
Removing the special-case Submenu component simplifies the API surface; all menus—root or nested—share the same props and logic. Nesting is inferred via context (presence of a parent floating node) so fewer concepts need to be learned.
Notes
- No new props are required; nesting works automatically.
- The deprecated
Submenustill renders (with a console warning in development) to ease incremental migration, but it will be removed in a future major version. - If you previously relied on custom styling targeting
Menu.Submenu, switch your selectors to target nestedMenuinstances (e.g. via an additional class or data attribute you apply yourself).
Quick Find & Replace Strategy
Regex suggestions (adapt as needed):
| Intent | Find | Replace |
|---|---|---|
| Open tag | <Menu\.Submenu> | <Menu> |
| Close tag | </Menu\.Submenu> | </Menu> |
Always review replacements manually—especially if you had additional props on Submenu
Migration steps checklist
- Replace all
[x, y]offsets with new object (or number) using mapping above. - Replace
auto-startandauto-endwith any of the other placements or adoptplacement="auto". - Re-test visual spacing (tune mainAxis).
- Adjust horizontal/vertical nudges via crossAxis or alignmentAxis.
- Remove obsolete tuple references.