Menu flyout
A menu item that opens a submenu in a flyout next to it, rather than expanding inline like a Collapsible. Use it inside any Menu — a Context menu, a dropdown, or a plain menu in a pane. Submenus may be nested arbitrarily deep.
The flyout opens on hover and on ArrowRight, Enter or click, and keeps itself within the browser window: when there is no room on the chosen side it flips to the opposite side and is clamped to the viewport.
A prediction cone keeps the submenu open while the pointer moves diagonally towards it, even when the cursor briefly passes over a sibling item — so reaching a submenu no longer requires a perfectly straight path. A matching return cone does the same on the way back, so heading from the submenu to its opener does not drop the submenu or activate the items in between. The cone can be visualised for debugging with the debug-cone prop on the surrounding Context menu or Menu.
Required icons
Props
disabled?: boolean
Disable the flyout trigger.
icon?: FluxIconName
An optional leading icon shown on the trigger.
is-active?: boolean
Renders the trigger in its active state.
is-destructive?: boolean
Renders the trigger in its destructive state.
label?: string
The label of the trigger.
position?: "right-top" | "left-top" | "…"
The side the submenu opens to. When there is no room it flips to the opposite side and is clamped to the viewport.
Default: right-top
Slots
default
The submenu content. Place a Menu with items (or more flyouts) here.
trigger
Replaces the trigger label with custom content.
Examples
Basic
A menu item that opens a submenu on hover or with the keyboard.
<template>
<div style="display: flex; flex-flow: column; gap: 15px">
<div style="display: flex; align-items: center; gap: 9px">
<FluxToggle
v-model="showCone"/>
Prediction cone
</div>
<FluxPane style="width: 300px">
<FluxMenu :debug-cone="showCone">
<FluxMenuGroup>
<FluxMenuItem
icon-leading="plus"
label="New file"/>
<FluxMenuFlyout
icon="arrow-up-from-square"
label="Export as">
<FluxMenu>
<FluxMenuGroup>
<FluxMenuItem label="PDF"/>
<FluxMenuItem label="PNG"/>
<FluxMenuItem label="SVG"/>
</FluxMenuGroup>
</FluxMenu>
</FluxMenuFlyout>
</FluxMenuGroup>
</FluxMenu>
</FluxPane>
</div>
</template>
<script
lang="ts"
setup>
import { FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxPane, FluxToggle } from '@flux-ui/components';
import { ref } from 'vue';
const showCone = ref(false);
</script>Nested
A submenu inside a submenu. There is no depth limit.
<template>
<div style="display: flex; flex-flow: column; gap: 15px">
<div style="display: flex; align-items: center; gap: 9px">
<FluxToggle
v-model="showCone"/>
Prediction cone
</div>
<FluxPane style="width: 300px">
<FluxMenu :debug-cone="showCone">
<FluxMenuGroup>
<FluxMenuItem
icon-leading="pen"
label="Edit"/>
<FluxMenuFlyout
icon="arrow-up-from-square"
label="Share">
<FluxMenu>
<FluxMenuGroup>
<FluxMenuItem
icon-leading="paper-plane"
label="Email"/>
<FluxMenuFlyout
icon="users"
label="Social">
<FluxMenu>
<FluxMenuGroup>
<FluxMenuItem label="Mastodon"/>
<FluxMenuItem label="Bluesky"/>
<FluxMenuItem label="LinkedIn"/>
</FluxMenuGroup>
</FluxMenu>
</FluxMenuFlyout>
<FluxMenuItem
icon-leading="copy"
label="Copy link"/>
</FluxMenuGroup>
</FluxMenu>
</FluxMenuFlyout>
</FluxMenuGroup>
</FluxMenu>
</FluxPane>
</div>
</template>
<script
lang="ts"
setup>
import { FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxPane, FluxToggle } from '@flux-ui/components';
import { ref } from 'vue';
const showCone = ref(false);
</script>Stacked
Several submenu openers under each other, like a Format menu in an editor.
<template>
<div style="display: flex; flex-flow: column; gap: 15px">
<div style="display: flex; align-items: center; gap: 9px">
<FluxToggle
v-model="showCone"/>
Prediction cone
</div>
<FluxPane style="width: 320px">
<FluxMenu :debug-cone="showCone">
<FluxMenuGroup>
<FluxMenuFlyout
icon="align-left"
label="Align">
<FluxMenu>
<FluxMenuGroup>
<FluxMenuItem
icon-leading="align-left"
label="Left"/>
<FluxMenuItem
icon-leading="align-center"
label="Center"/>
<FluxMenuItem
icon-leading="align-right"
label="Right"/>
<FluxMenuItem
icon-leading="align-justify"
label="Justify"/>
</FluxMenuGroup>
</FluxMenu>
</FluxMenuFlyout>
<FluxMenuFlyout
icon="list-ul"
label="List">
<FluxMenu>
<FluxMenuGroup>
<FluxMenuItem
icon-leading="list-ul"
label="Bulleted"/>
<FluxMenuItem
icon-leading="list-ol"
label="Numbered"/>
</FluxMenuGroup>
</FluxMenu>
</FluxMenuFlyout>
<FluxMenuFlyout
icon="indent"
label="Indentation">
<FluxMenu>
<FluxMenuGroup>
<FluxMenuItem
icon-leading="indent"
label="Increase"/>
<FluxMenuItem
icon-leading="outdent"
label="Decrease"/>
</FluxMenuGroup>
</FluxMenu>
</FluxMenuFlyout>
<FluxMenuFlyout
icon="image"
label="Insert">
<FluxMenu>
<FluxMenuGroup>
<FluxMenuItem
icon-leading="image"
label="Image"/>
<FluxMenuItem
icon-leading="play"
label="Video"/>
</FluxMenuGroup>
</FluxMenu>
</FluxMenuFlyout>
</FluxMenuGroup>
</FluxMenu>
</FluxPane>
</div>
</template>
<script
lang="ts"
setup>
import { FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxPane, FluxToggle } from '@flux-ui/components';
import { ref } from 'vue';
const showCone = ref(false);
</script>