---
name: mega-menu
title: Mega Menu
url: https://ui.frontic.com/components/ui/mega-menu.md
tier: ui
install: npx @frontic/ui add mega-menu
files:
  - components/ui/mega-menu/MegaMenu.vue
  - components/ui/mega-menu/MegaMenuContent.vue
  - components/ui/mega-menu/MegaMenuItem.vue
  - components/ui/mega-menu/MegaMenuLink.vue
  - components/ui/mega-menu/MegaMenuList.vue
  - components/ui/mega-menu/MegaMenuTrigger.vue
  - components/ui/mega-menu/MegaMenuViewport.vue
  - components/ui/mega-menu/index.ts
dependencies:
  - "@lucide/vue"
  - "@vueuse/core"
  - reka-ui
---

# Mega Menu

Store navigation whose flyouts span the bar's full width, for link columns and featured tiles; panels slide between categories and the height follows.

## Install

```sh
npx @frontic/ui add mega-menu
```

Used by: [Navigation Shop](https://ui.frontic.com/components/sections/navigation-shop.md)

npm packages: `@lucide/vue`, `@vueuse/core`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { Image } from '@/components/ui/image'
import { MegaMenu, MegaMenuContent, MegaMenuItem, MegaMenuLink, MegaMenuList, MegaMenuTrigger, MegaMenuViewport } from '@/components/ui/mega-menu'

const image = (name: string) => `https://static.frontic.com/cdn-cgi/image/width=640,format=auto/ui/products/${name}.jpg`

const menus = [
  {
    name: 'Women',
    columns: [
      { name: 'Winter', links: ['Jackets', 'Beanies', 'Goggles', 'Helmets', 'Gloves'] },
      { name: 'Equipment', links: ['Backpacks'] },
    ],
    featured: { name: 'Winterwonder Parka', image: image('winterwonder-parka') },
  },
  {
    name: 'Men',
    columns: [
      { name: 'Winter', links: ['Jackets', 'Beanies', 'Goggles', 'Helmets'] },
      { name: 'Equipment', links: ['Backpacks'] },
    ],
    featured: { name: 'Frost Escape Parka', image: image('frost-escape-parka') },
  },
]
</script>

<template>
  <div class="min-h-110 w-4xl max-w-full">
    <MegaMenu default-value="Women">
      <MegaMenuList>
        <MegaMenuItem v-for="menu in menus" :key="menu.name" :value="menu.name">
          <MegaMenuTrigger class="h-9 px-4">{{ menu.name }}</MegaMenuTrigger>
          <MegaMenuContent>
            <div class="grid grid-cols-2 gap-6 p-6 @2xl:grid-cols-3 @2xl:gap-8 @2xl:p-8">
              <div v-for="column in menu.columns" :key="column.name" class="flex flex-col gap-3">
                <p class="text-muted-foreground">{{ column.name }}</p>
                <MegaMenuLink v-for="link in column.links" :key="link" href="#">{{ link }}</MegaMenuLink>
              </div>
              <MegaMenuLink href="#" class="flex flex-col gap-3">
                <Image :src="menu.featured.image" :alt="menu.featured.name" class="aspect-square w-full object-cover" />
                {{ menu.featured.name }}
              </MegaMenuLink>
            </div>
          </MegaMenuContent>
        </MegaMenuItem>
        <MegaMenuItem>
          <MegaMenuLink href="#" class="inline-flex h-9 items-center px-4">Sale</MegaMenuLink>
        </MegaMenuItem>
      </MegaMenuList>
      <MegaMenuViewport />
    </MegaMenu>
  </div>
</template>
```

The example also uses `image`, which `add mega-menu` leaves out: `npx @frontic/ui add image`.

## Anatomy

```vue
<MegaMenu>
  <MegaMenuList>
    <MegaMenuItem>
      <MegaMenuTrigger />
      <MegaMenuContent>
        <MegaMenuLink />
      </MegaMenuContent>
      <MegaMenuLink />
    </MegaMenuItem>
  </MegaMenuList>
  <MegaMenuViewport />
</MegaMenu>
```

## API reference

### MegaMenu

Groups all parts of the mega menu. Renders a `<nav>`.

`data-slot="mega-menu"` · `MegaMenu.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `modelValue` | `string` |  | The controlled value of the menu item to activate. Can be used as `v-model`. |
| `defaultValue` | `string` |  | The value of the menu item that should be active when initially rendered.<br><br>Use when you do not need to control the value state. |
| `dir` | `Direction`: `'ltr' \| 'rtl'` |  | The reading direction of the combobox when applicable.<br><br> If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
| `orientation` | `Orientation`: `'vertical' \| 'horizontal'` | `'horizontal'` | The orientation of the menu. |
| `delayDuration` | `number` | `200` | The duration from when the pointer enters the trigger until the tooltip gets opened. |
| `skipDelayDuration` | `number` | `300` | How much time a user has to enter another trigger without incurring a delay again. |
| `disableClickTrigger` | `boolean` | `false` | If `true`, menu cannot be open by click on trigger |
| `disableHoverTrigger` | `boolean` | `false` | If `true`, menu cannot be open by hover on trigger |
| `disablePointerLeaveClose` | `boolean` | `false` | If `true`, menu will not close during pointer leave event |
| `unmountOnHide` | `boolean` | `true` | When `true`, the element will be unmounted on closed state. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'nav'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `value: string` | Event handler called when the value changes. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `default` | `{ modelValue: string; }` | Holds the mega menu's items; gets the value of the open item as `modelValue`. |

### MegaMenuContent

Holds the links and content shown when its menu item is open.

`data-slot="mega-menu-content"` · `MegaMenuContent.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `forceMount` | `boolean` | `false` | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| `disableOutsidePointerEvents` | `boolean` | `false` | When `true`, hover/focus/click interactions will be disabled on elements outside the `DismissableLayer`. Users will need to click twice on outside elements to interact with them: once to close the `DismissableLayer`, and again to trigger the element. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `escapeKeyDown` | `event: KeyboardEvent` | Event handler called when the escape key is down. Can be prevented. |
| `pointerDownOutside` | `event: PointerDownOutsideEvent` | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
| `focusOutside` | `event: FocusOutsideEvent` | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
| `interactOutside` | `event: PointerDownOutsideEvent \| FocusOutsideEvent` | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown` event happens outside or focus moves outside of it. Can be prevented. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-dismissable-layer` |  | Marks a layer that closes on Escape or a click outside it. |
| `data-motion` | `'from-start' \| 'from-end' \| 'to-start' \| 'to-end'` | The direction the content animates in from or out to as the active item changes. |
| `data-orientation` | `'horizontal' \| 'vertical'` | The direction the part is laid out in. |
| `data-state` | `'open' \| 'closed'` | The state the part is in, one value per state. |

### MegaMenuItem

Groups a trigger with the content it opens in the mega menu. Renders a `<li>`.

`data-slot="mega-menu-item"` · `MegaMenuItem.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` |  | A unique value that associates the item with an active value when the navigation menu is controlled.<br><br> This prop is managed automatically when uncontrolled. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'li'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-menu-item` |  | Marks the item. |

### MegaMenuLink

Links to a page from inside the mega menu. Renders a `<a>`.

`data-slot="mega-menu-link"` · `MegaMenuLink.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `active` | `boolean` | `false` | Used to identify the link as the currently active page. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'a'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `select` | `payload: CustomEvent<{ originalEvent: Event; }>` | Event handler called when the user selects a link (via mouse or keyboard). Calling `event.preventDefault` in this handler will prevent the navigation menu from closing when selecting that link. |

### MegaMenuList

Lists the top-level items of the mega menu. Renders a `<ul>`.

`data-slot="mega-menu-list"` · `MegaMenuList.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'ul'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-orientation` | `'horizontal' \| 'vertical'` | The direction the part is laid out in. |

### MegaMenuTrigger

Opens and closes the content of its mega menu item. Renders a `<button>`.

`data-slot="mega-menu-trigger"` · `MegaMenuTrigger.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | When `true`, prevents the user from interacting with item |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'button'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `chevron` | `boolean` | `true` | Shows a chevron after the label that turns while the panel is open. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-navigation-menu-trigger` |  | Marks the trigger. |
| `data-state` | `'open' \| 'closed'` | The state the part is in, one value per state. |

### MegaMenuViewport

Shows the content of the open mega menu item.

`data-slot="mega-menu-viewport"` · `MegaMenuViewport.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `forceMount` | `boolean` | `false` | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| `align` | `'center' \| 'start' \| 'end'` | `'center'` | Placement of the viewport for css variables `(--reka-navigation-menu-viewport-left, --reka-navigation-menu-viewport-top)`. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-orientation` | `'horizontal' \| 'vertical'` | The direction the part is laid out in. |
| `data-state` | `'open' \| 'closed'` | The state the part is in, one value per state. |

| CSS variable | Description |
| --- | --- |
| `--reka-navigation-menu-viewport-height` | The open content's height, so the viewport can animate to it. |
| `--reka-navigation-menu-viewport-left` | The open content's left offset, in pixels. |
| `--reka-navigation-menu-viewport-top` | The open content's top offset, in pixels. |
| `--reka-navigation-menu-viewport-width` | The open content's width, so the viewport can animate to it. |

## Styling

| Part | Recipe |
| --- | --- |
| `list` | [`mega-menu-list`](https://ui.frontic.com/DESIGN.md#mega-menu-list) |
| `item` | [`mega-menu-item`](https://ui.frontic.com/DESIGN.md#mega-menu-item) |
| `default` | [`mega-menu`](https://ui.frontic.com/DESIGN.md#mega-menu) |
| `trigger` | [`mega-menu-trigger`](https://ui.frontic.com/DESIGN.md#mega-menu-trigger) |
| `content` | [`mega-menu-content`](https://ui.frontic.com/DESIGN.md#mega-menu-content) |
| `viewport` | [`mega-menu-viewport`](https://ui.frontic.com/DESIGN.md#mega-menu-viewport) |
| `focus-ring` | [`focus-ring`](https://ui.frontic.com/DESIGN.md#focus-ring) |

States with a look of their own: focus.

## Source

`components/ui/mega-menu/MegaMenu.vue`

```vue
<script lang="ts">
/** Groups all parts of the mega menu. */
export default {}
</script>

<script setup lang="ts">
import type { NavigationMenuRootEmits, NavigationMenuRootProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { NavigationMenuRoot, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<
  NavigationMenuRootProps & {
    class?: HTMLAttributes['class']
  }
>()
const emits = defineEmits<NavigationMenuRootEmits>()

const delegatedProps = reactiveOmit(props, 'class')
const forwarded = useForwardPropsEmits(delegatedProps, emits)

defineSlots<{
  /** Holds the mega menu's items; gets the value of the open item as `modelValue`. */
  default?: (props: { modelValue: string }) => unknown
}>()
</script>

<template>
  <NavigationMenuRoot v-slot="slotProps" data-slot="mega-menu" v-bind="forwarded" :class="['fui-mega-menu relative', props.class]">
    <slot v-bind="slotProps" />
  </NavigationMenuRoot>
</template>
```

`components/ui/mega-menu/MegaMenuContent.vue`

```vue
<script lang="ts">
/** Holds the links and content shown when its menu item is open. */
export default {}
</script>

<script setup lang="ts">
import type { NavigationMenuContentEmits, NavigationMenuContentProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { NavigationMenuContent, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<
  NavigationMenuContentProps & {
    class?: HTMLAttributes['class']
  }
>()
const emits = defineEmits<NavigationMenuContentEmits>()

const delegatedProps = reactiveOmit(props, 'class')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <NavigationMenuContent
    data-slot="mega-menu-content"
    v-bind="forwarded"
    :class="[
      'fui-mega-menu-content absolute inset-x-0 top-0 data-motion-from:animate-in data-motion-to:animate-out data-motion-from:fade-in data-motion-to:fade-out data-motion-from-end:slide-in-from-end-8 data-motion-from-start:slide-in-from-start-8 data-motion-to-end:slide-out-to-end-8 data-motion-to-start:slide-out-to-start-8',
      props.class,
    ]"
  >
    <slot />
  </NavigationMenuContent>
</template>
```

`components/ui/mega-menu/MegaMenuItem.vue`

```vue
<script lang="ts">
/** Groups a trigger with the content it opens in the mega menu. */
export default {}
</script>

<script setup lang="ts">
import type { NavigationMenuItemProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { NavigationMenuItem, useForwardProps } from 'reka-ui'

const props = defineProps<NavigationMenuItemProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')
const forwarded = useForwardProps(delegatedProps)
</script>

<template>
  <NavigationMenuItem data-slot="mega-menu-item" v-bind="forwarded" :class="['fui-mega-menu-item static', props.class]">
    <slot />
  </NavigationMenuItem>
</template>
```

`components/ui/mega-menu/MegaMenuLink.vue`

```vue
<script lang="ts">
/** Links to a page from inside the mega menu. */
export default {}
</script>

<script setup lang="ts">
import type { NavigationMenuLinkEmits, NavigationMenuLinkProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { NavigationMenuLink, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<NavigationMenuLinkProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<NavigationMenuLinkEmits>()

const delegatedProps = reactiveOmit(props, 'class')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <NavigationMenuLink data-slot="mega-menu-link" v-bind="forwarded" :class="['transition-colors outline-none focus-visible:fui-focus-ring', props.class]">
    <slot />
  </NavigationMenuLink>
</template>
```

`components/ui/mega-menu/MegaMenuList.vue`

```vue
<script lang="ts">
/** Lists the top-level items of the mega menu. */
export default {}
</script>

<script setup lang="ts">
import type { NavigationMenuListProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { NavigationMenuList, useForwardProps } from 'reka-ui'

const props = defineProps<NavigationMenuListProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')
const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <NavigationMenuList data-slot="mega-menu-list" v-bind="forwardedProps" :class="['fui-mega-menu-list list-none', props.class]">
    <slot />
  </NavigationMenuList>
</template>
```

`components/ui/mega-menu/MegaMenuTrigger.vue`

```vue
<script lang="ts">
/** Opens and closes the content of its mega menu item. */
export default {}
</script>

<script setup lang="ts">
import { ChevronDownIcon } from '@lucide/vue'

import type { NavigationMenuTriggerProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { NavigationMenuTrigger, useForwardProps } from 'reka-ui'

const props = withDefaults(
  defineProps<
    NavigationMenuTriggerProps & {
      /** Shows a chevron after the label that turns while the panel is open. */
      chevron?: boolean
      class?: HTMLAttributes['class']
    }
  >(),
  { chevron: true },
)

const delegatedProps = reactiveOmit(props, 'class', 'chevron')
const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <NavigationMenuTrigger
    data-slot="mega-menu-trigger"
    v-bind="forwardedProps"
    :class="['fui-mega-menu-trigger group/mega-menu-trigger transition-colors outline-none focus-visible:fui-focus-ring', props.class]"
  >
    <slot />
    <ChevronDownIcon v-if="props.chevron" data-icon class="relative top-px ms-1 transition duration-300 group-aria-expanded/mega-menu-trigger:rotate-180" aria-hidden="true" />
  </NavigationMenuTrigger>
</template>
```

`components/ui/mega-menu/MegaMenuViewport.vue`

```vue
<script lang="ts">
/** Shows the content of the open mega menu item. */
export default {}
</script>

<script setup lang="ts">
import type { NavigationMenuViewportProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { NavigationMenuViewport, useForwardProps } from 'reka-ui'

const props = defineProps<NavigationMenuViewportProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')
const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <NavigationMenuViewport
    data-slot="mega-menu-viewport"
    v-bind="forwardedProps"
    :class="[
      'fui-mega-menu-viewport absolute inset-x-0 top-full z-50 origin-top overflow-hidden transition-all duration-200 data-open:animate-in data-closed:animate-out data-open:fade-in-0 data-closed:fade-out-0 data-open:zoom-in-98 data-closed:zoom-out-98',
      props.class,
    ]"
  />
</template>
```

## API of the items the example uses

### Image

[Full docs](https://ui.frontic.com/components/ui/image.md) · `npx @frontic/ui add image`

#### Image

Shows an image with a loading state and a fallback if it fails to load. Renders a `<span>`.

`data-slot="image"` · `Image.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `src` | `string` |  | URL of the image to show. |
| `alt` | `string` |  | Text alternative read by screen readers; leave empty for a purely decorative image. |
| `loadingEffect` | `LoadingEffect`: `'skeleton' \| 'blur' \| 'none'` | `'skeleton'` | What shows until the image has loaded: a pulsing placeholder, the `placeholderSrc` preview blurred, or nothing. Blur without a `placeholderSrc` shows the pulsing placeholder. |
| `fallbackSrc` | `string` |  | URL tried when `src` fails to load; the fallback slot shows when both fail. |
| `placeholderSrc` | `string` |  | URL of a tiny version of the image, e.g. 24px wide, shown blurred while `src` loads when `loadingEffect` is `blur`. |
| `priority` | `boolean` | `false` | Loads the image first, eagerly and at high fetch priority, instead of when it nears the viewport. Set it on the one image that is the page's largest paint, such as a hero or the first gallery image. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `fallback` |  | Replaces the placeholder shown when the image fails to load and no fallback URL loads either. |

#### ImageFallback

Shows a placeholder in place of an image that failed to load. Renders a `<span>`.

`data-slot="image-fallback"` · `ImageFallback.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
