ContentNavigationPRO

An accordion-style navigation component for organizing page links.
This component is only available when the @nuxt/content module is installed.

Usage

Use the navigation prop with the navigation value you get when fetching the navigation of your app.

<script setup lang="ts">
import type { ContentNavigationItem } from '@nuxt/content'

const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
</script>

<template>
  <UContentNavigation :navigation="navigation" highlight />
</template>

Type

Set the type prop to single to only allow one item to be open at a time. Defaults to multiple.

<script setup lang="ts">
const navigation = ref([
  {
    title: 'Guide',
    icon: 'i-lucide-book-open',
    path: '#getting-started',
    children: [
      {
        title: 'Introduction',
        path: '#introduction',
        active: true
      },
      {
        title: 'Installation',
        path: '#installation'
      }
    ]
  },
  {
    title: 'Composables',
    icon: 'i-lucide-database',
    path: '#composables',
    children: [
      {
        title: 'defineShortcuts',
        path: '#defineshortcuts'
      },
      {
        title: 'useModal',
        path: '#usemodal'
      }
    ]
  }
])
</script>

<template>
  <UContentNavigation type="single" />
</template>

Color

Use the color prop to change the color of the navigation links.

<script setup lang="ts">
const navigation = ref([
  {
    title: 'Guide',
    icon: 'i-lucide-book-open',
    path: '#getting-started',
    children: [
      {
        title: 'Introduction',
        path: '#introduction',
        active: true
      },
      {
        title: 'Installation',
        path: '#installation'
      }
    ]
  },
  {
    title: 'Composables',
    icon: 'i-lucide-database',
    path: '#composables',
    children: [
      {
        title: 'defineShortcuts',
        path: '#defineshortcuts'
      },
      {
        title: 'useModal',
        path: '#usemodal'
      }
    ]
  }
])
</script>

<template>
  <UContentNavigation color="neutral" />
</template>

Variant

Use the variant prop to change the variant of the navigation links.

<script setup lang="ts">
const navigation = ref([
  {
    title: 'Guide',
    icon: 'i-lucide-book-open',
    path: '#getting-started',
    children: [
      {
        title: 'Introduction',
        path: '#introduction',
        active: true
      },
      {
        title: 'Installation',
        path: '#installation'
      }
    ]
  },
  {
    title: 'Composables',
    icon: 'i-lucide-database',
    path: '#composables',
    children: [
      {
        title: 'defineShortcuts',
        path: '#defineshortcuts'
      },
      {
        title: 'useModal',
        path: '#usemodal'
      }
    ]
  }
])
</script>

<template>
  <UContentNavigation variant="link" />
</template>

Highlight

Use the highlight prop to display a highlighted border for the active link.

Use the highlight-color prop to change the color of the border. It defaults to the color prop.

<script setup lang="ts">
const navigation = ref([
  {
    title: 'Guide',
    icon: 'i-lucide-book-open',
    path: '#getting-started',
    children: [
      {
        title: 'Introduction',
        path: '#introduction',
        active: true
      },
      {
        title: 'Installation',
        path: '#installation'
      }
    ]
  },
  {
    title: 'Composables',
    icon: 'i-lucide-database',
    path: '#composables',
    children: [
      {
        title: 'defineShortcuts',
        path: '#defineshortcuts'
      },
      {
        title: 'useModal',
        path: '#usemodal'
      }
    ]
  }
])
</script>

<template>
  <UContentNavigation highlight highlight-color="primary" color="primary" variant="pill" />
</template>

Trailing Icon

<script setup lang="ts">
const navigation = ref([
  {
    title: 'Guide',
    icon: 'i-lucide-book-open',
    path: '#getting-started',
    children: [
      {
        title: 'Introduction',
        path: '#introduction',
        active: true
      },
      {
        title: 'Installation',
        path: '#installation'
      }
    ]
  },
  {
    title: 'Composables',
    icon: 'i-lucide-database',
    path: '#composables',
    children: [
      {
        title: 'defineShortcuts',
        path: '#defineshortcuts'
      },
      {
        title: 'useModal',
        path: '#usemodal'
      }
    ]
  }
])
</script>

<template>
  <UContentNavigation trailing-icon="i-lucide-arrow-up" />
</template>

Examples

Within a layout

Use the ContentNavigation component inside a PageAside component within a layout to display the navigation of the page:

layouts/docs.vue
<script setup lang="ts">
import type { ContentNavigationItem } from '@nuxt/content'

const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
</script>

<template>
  <UPage>
    <template #left>
      <UPageAside>
        <UContentNavigation :navigation="navigation" highlight />
      </UPageAside>
    </template>

    <slot />
  </UPage>
</template>

Within a header

Use the ContentNavigation component inside the content slot of a Header component to display the navigation of the page on mobile:

components/Header.vue
<script setup lang="ts">
import type { ContentNavigationItem } from '@nuxt/content'

const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
</script>

<template>
  <UHeader>
    <template #body>
      <UContentNavigation :navigation="navigation" highlight />
    </template>
  </UHeader>
</template>

API

Props

Prop Default Type

Slots

Slot Type
link

{ link: ContentNavigationLink; active?: boolean | undefined; }

link-leading

{ link: ContentNavigationLink; active?: boolean | undefined; }

link-title

{ link: ContentNavigationLink; active?: boolean | undefined; }

link-trailing

{ link: ContentNavigationLink; active?: boolean | undefined; }

Emits

Event Type
update:modelValue

string | string[]

Theme

app.config.ts
export default defineAppConfig({
  uiPro: {
    contentNavigation: {
      slots: {
        root: '',
        content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
        list: 'isolate -mx-2.5 -mt-1.5',
        item: '',
        listWithChildren: 'ms-5 border-s border-(--ui-border)',
        itemWithChildren: 'flex flex-col data-[state=open]:mb-1.5',
        trigger: 'font-semibold',
        link: 'group relative w-full px-2.5 py-1.5 before:inset-y-px before:inset-x-0 flex items-center gap-1.5 text-sm before:absolute before:z-[-1] before:rounded-[calc(var(--ui-radius)*1.5)] focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
        linkLeadingIcon: 'shrink-0 size-5',
        linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
        linkTrailingBadge: 'shrink-0',
        linkTrailingBadgeSize: 'sm',
        linkTrailingIcon: 'size-5 transform transition-transform duration-200 shrink-0 group-data-[state=open]:rotate-180',
        linkTitle: 'truncate',
        linkTitleExternalIcon: 'size-3 align-top text-(--ui-text-dimmed)'
      },
      variants: {
        color: {
          primary: {
            trigger: 'focus-visible:ring-(--ui-primary)',
            link: 'focus-visible:before:ring-(--ui-primary)'
          },
          secondary: {
            trigger: 'focus-visible:ring-(--ui-secondary)',
            link: 'focus-visible:before:ring-(--ui-secondary)'
          },
          success: {
            trigger: 'focus-visible:ring-(--ui-success)',
            link: 'focus-visible:before:ring-(--ui-success)'
          },
          info: {
            trigger: 'focus-visible:ring-(--ui-info)',
            link: 'focus-visible:before:ring-(--ui-info)'
          },
          warning: {
            trigger: 'focus-visible:ring-(--ui-warning)',
            link: 'focus-visible:before:ring-(--ui-warning)'
          },
          error: {
            trigger: 'focus-visible:ring-(--ui-error)',
            link: 'focus-visible:before:ring-(--ui-error)'
          },
          neutral: {
            trigger: 'focus-visible:ring-(--ui-border-inverted)',
            link: 'focus-visible:before:ring-(--ui-border-inverted)'
          }
        },
        highlightColor: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        variant: {
          pill: '',
          link: ''
        },
        active: {
          true: {
            link: 'font-medium'
          },
          false: {
            link: 'text-(--ui-text-muted)',
            linkLeadingIcon: 'text-(--ui-text-dimmed)'
          }
        },
        disabled: {
          true: {
            link: 'cursor-not-allowed opacity-75'
          }
        },
        highlight: {
          true: {}
        },
        level: {
          true: {
            item: 'ps-1.5 -ms-px',
            itemWithChildren: 'ps-1.5 -ms-px'
          }
        }
      },
      compoundVariants: [
        {
          highlight: true,
          level: true,
          class: {
            link: [
              'after:absolute after:-left-1.5 after:inset-y-0.5 after:block after:w-px after:rounded-full',
              'after:transition-colors'
            ]
          }
        },
        {
          disabled: false,
          active: false,
          variant: 'pill',
          class: {
            link: [
              'hover:text-(--ui-text-highlighted) hover:before:bg-(--ui-bg-elevated)/50 data-[state=open]:text-(--ui-text-highlighted)',
              'transition-colors before:transition-colors'
            ],
            linkLeadingIcon: [
              'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)',
              'transition-colors'
            ]
          }
        },
        {
          color: 'primary',
          variant: 'pill',
          active: true,
          class: {
            link: 'text-(--ui-primary)',
            linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)'
          }
        },
        {
          color: 'neutral',
          variant: 'pill',
          active: true,
          class: {
            link: 'text-(--ui-text-highlighted)',
            linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)'
          }
        },
        {
          variant: 'pill',
          active: true,
          highlight: false,
          class: {
            link: 'before:bg-(--ui-bg-elevated)'
          }
        },
        {
          variant: 'pill',
          active: true,
          highlight: true,
          class: {
            link: [
              'hover:before:bg-(--ui-bg-elevated)/50',
              'before:transition-colors'
            ]
          }
        },
        {
          disabled: false,
          active: false,
          variant: 'link',
          class: {
            link: [
              'hover:text-(--ui-text-highlighted) data-[state=open]:text-(--ui-text-highlighted)',
              'transition-colors'
            ],
            linkLeadingIcon: [
              'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)',
              'transition-colors'
            ]
          }
        },
        {
          color: 'primary',
          variant: 'link',
          active: true,
          class: {
            link: 'text-(--ui-primary)',
            linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)'
          }
        },
        {
          color: 'neutral',
          variant: 'link',
          active: true,
          class: {
            link: 'text-(--ui-text-highlighted)',
            linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)'
          }
        },
        {
          highlightColor: 'primary',
          highlight: true,
          level: true,
          active: true,
          class: {
            link: 'after:bg-(--ui-primary)'
          }
        },
        {
          highlightColor: 'neutral',
          highlight: true,
          level: true,
          active: true,
          class: {
            link: 'after:bg-(--ui-bg-inverted)'
          }
        }
      ],
      defaultVariants: {
        color: 'primary',
        highlightColor: 'primary',
        variant: 'pill'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      uiPro: {
        contentNavigation: {
          slots: {
            root: '',
            content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
            list: 'isolate -mx-2.5 -mt-1.5',
            item: '',
            listWithChildren: 'ms-5 border-s border-(--ui-border)',
            itemWithChildren: 'flex flex-col data-[state=open]:mb-1.5',
            trigger: 'font-semibold',
            link: 'group relative w-full px-2.5 py-1.5 before:inset-y-px before:inset-x-0 flex items-center gap-1.5 text-sm before:absolute before:z-[-1] before:rounded-[calc(var(--ui-radius)*1.5)] focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
            linkLeadingIcon: 'shrink-0 size-5',
            linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
            linkTrailingBadge: 'shrink-0',
            linkTrailingBadgeSize: 'sm',
            linkTrailingIcon: 'size-5 transform transition-transform duration-200 shrink-0 group-data-[state=open]:rotate-180',
            linkTitle: 'truncate',
            linkTitleExternalIcon: 'size-3 align-top text-(--ui-text-dimmed)'
          },
          variants: {
            color: {
              primary: {
                trigger: 'focus-visible:ring-(--ui-primary)',
                link: 'focus-visible:before:ring-(--ui-primary)'
              },
              secondary: {
                trigger: 'focus-visible:ring-(--ui-secondary)',
                link: 'focus-visible:before:ring-(--ui-secondary)'
              },
              success: {
                trigger: 'focus-visible:ring-(--ui-success)',
                link: 'focus-visible:before:ring-(--ui-success)'
              },
              info: {
                trigger: 'focus-visible:ring-(--ui-info)',
                link: 'focus-visible:before:ring-(--ui-info)'
              },
              warning: {
                trigger: 'focus-visible:ring-(--ui-warning)',
                link: 'focus-visible:before:ring-(--ui-warning)'
              },
              error: {
                trigger: 'focus-visible:ring-(--ui-error)',
                link: 'focus-visible:before:ring-(--ui-error)'
              },
              neutral: {
                trigger: 'focus-visible:ring-(--ui-border-inverted)',
                link: 'focus-visible:before:ring-(--ui-border-inverted)'
              }
            },
            highlightColor: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            variant: {
              pill: '',
              link: ''
            },
            active: {
              true: {
                link: 'font-medium'
              },
              false: {
                link: 'text-(--ui-text-muted)',
                linkLeadingIcon: 'text-(--ui-text-dimmed)'
              }
            },
            disabled: {
              true: {
                link: 'cursor-not-allowed opacity-75'
              }
            },
            highlight: {
              true: {}
            },
            level: {
              true: {
                item: 'ps-1.5 -ms-px',
                itemWithChildren: 'ps-1.5 -ms-px'
              }
            }
          },
          compoundVariants: [
            {
              highlight: true,
              level: true,
              class: {
                link: [
                  'after:absolute after:-left-1.5 after:inset-y-0.5 after:block after:w-px after:rounded-full',
                  'after:transition-colors'
                ]
              }
            },
            {
              disabled: false,
              active: false,
              variant: 'pill',
              class: {
                link: [
                  'hover:text-(--ui-text-highlighted) hover:before:bg-(--ui-bg-elevated)/50 data-[state=open]:text-(--ui-text-highlighted)',
                  'transition-colors before:transition-colors'
                ],
                linkLeadingIcon: [
                  'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)',
                  'transition-colors'
                ]
              }
            },
            {
              color: 'primary',
              variant: 'pill',
              active: true,
              class: {
                link: 'text-(--ui-primary)',
                linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)'
              }
            },
            {
              color: 'neutral',
              variant: 'pill',
              active: true,
              class: {
                link: 'text-(--ui-text-highlighted)',
                linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)'
              }
            },
            {
              variant: 'pill',
              active: true,
              highlight: false,
              class: {
                link: 'before:bg-(--ui-bg-elevated)'
              }
            },
            {
              variant: 'pill',
              active: true,
              highlight: true,
              class: {
                link: [
                  'hover:before:bg-(--ui-bg-elevated)/50',
                  'before:transition-colors'
                ]
              }
            },
            {
              disabled: false,
              active: false,
              variant: 'link',
              class: {
                link: [
                  'hover:text-(--ui-text-highlighted) data-[state=open]:text-(--ui-text-highlighted)',
                  'transition-colors'
                ],
                linkLeadingIcon: [
                  'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)',
                  'transition-colors'
                ]
              }
            },
            {
              color: 'primary',
              variant: 'link',
              active: true,
              class: {
                link: 'text-(--ui-primary)',
                linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)'
              }
            },
            {
              color: 'neutral',
              variant: 'link',
              active: true,
              class: {
                link: 'text-(--ui-text-highlighted)',
                linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)'
              }
            },
            {
              highlightColor: 'primary',
              highlight: true,
              level: true,
              active: true,
              class: {
                link: 'after:bg-(--ui-primary)'
              }
            },
            {
              highlightColor: 'neutral',
              highlight: true,
              level: true,
              active: true,
              class: {
                link: 'after:bg-(--ui-bg-inverted)'
              }
            }
          ],
          defaultVariants: {
            color: 'primary',
            highlightColor: 'primary',
            variant: 'pill'
          }
        }
      }
    })
  ]
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'

export default defineConfig({
  plugins: [
    vue(),
    uiPro({
      uiPro: {
        contentNavigation: {
          slots: {
            root: '',
            content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
            list: 'isolate -mx-2.5 -mt-1.5',
            item: '',
            listWithChildren: 'ms-5 border-s border-(--ui-border)',
            itemWithChildren: 'flex flex-col data-[state=open]:mb-1.5',
            trigger: 'font-semibold',
            link: 'group relative w-full px-2.5 py-1.5 before:inset-y-px before:inset-x-0 flex items-center gap-1.5 text-sm before:absolute before:z-[-1] before:rounded-[calc(var(--ui-radius)*1.5)] focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
            linkLeadingIcon: 'shrink-0 size-5',
            linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
            linkTrailingBadge: 'shrink-0',
            linkTrailingBadgeSize: 'sm',
            linkTrailingIcon: 'size-5 transform transition-transform duration-200 shrink-0 group-data-[state=open]:rotate-180',
            linkTitle: 'truncate',
            linkTitleExternalIcon: 'size-3 align-top text-(--ui-text-dimmed)'
          },
          variants: {
            color: {
              primary: {
                trigger: 'focus-visible:ring-(--ui-primary)',
                link: 'focus-visible:before:ring-(--ui-primary)'
              },
              secondary: {
                trigger: 'focus-visible:ring-(--ui-secondary)',
                link: 'focus-visible:before:ring-(--ui-secondary)'
              },
              success: {
                trigger: 'focus-visible:ring-(--ui-success)',
                link: 'focus-visible:before:ring-(--ui-success)'
              },
              info: {
                trigger: 'focus-visible:ring-(--ui-info)',
                link: 'focus-visible:before:ring-(--ui-info)'
              },
              warning: {
                trigger: 'focus-visible:ring-(--ui-warning)',
                link: 'focus-visible:before:ring-(--ui-warning)'
              },
              error: {
                trigger: 'focus-visible:ring-(--ui-error)',
                link: 'focus-visible:before:ring-(--ui-error)'
              },
              neutral: {
                trigger: 'focus-visible:ring-(--ui-border-inverted)',
                link: 'focus-visible:before:ring-(--ui-border-inverted)'
              }
            },
            highlightColor: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            variant: {
              pill: '',
              link: ''
            },
            active: {
              true: {
                link: 'font-medium'
              },
              false: {
                link: 'text-(--ui-text-muted)',
                linkLeadingIcon: 'text-(--ui-text-dimmed)'
              }
            },
            disabled: {
              true: {
                link: 'cursor-not-allowed opacity-75'
              }
            },
            highlight: {
              true: {}
            },
            level: {
              true: {
                item: 'ps-1.5 -ms-px',
                itemWithChildren: 'ps-1.5 -ms-px'
              }
            }
          },
          compoundVariants: [
            {
              highlight: true,
              level: true,
              class: {
                link: [
                  'after:absolute after:-left-1.5 after:inset-y-0.5 after:block after:w-px after:rounded-full',
                  'after:transition-colors'
                ]
              }
            },
            {
              disabled: false,
              active: false,
              variant: 'pill',
              class: {
                link: [
                  'hover:text-(--ui-text-highlighted) hover:before:bg-(--ui-bg-elevated)/50 data-[state=open]:text-(--ui-text-highlighted)',
                  'transition-colors before:transition-colors'
                ],
                linkLeadingIcon: [
                  'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)',
                  'transition-colors'
                ]
              }
            },
            {
              color: 'primary',
              variant: 'pill',
              active: true,
              class: {
                link: 'text-(--ui-primary)',
                linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)'
              }
            },
            {
              color: 'neutral',
              variant: 'pill',
              active: true,
              class: {
                link: 'text-(--ui-text-highlighted)',
                linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)'
              }
            },
            {
              variant: 'pill',
              active: true,
              highlight: false,
              class: {
                link: 'before:bg-(--ui-bg-elevated)'
              }
            },
            {
              variant: 'pill',
              active: true,
              highlight: true,
              class: {
                link: [
                  'hover:before:bg-(--ui-bg-elevated)/50',
                  'before:transition-colors'
                ]
              }
            },
            {
              disabled: false,
              active: false,
              variant: 'link',
              class: {
                link: [
                  'hover:text-(--ui-text-highlighted) data-[state=open]:text-(--ui-text-highlighted)',
                  'transition-colors'
                ],
                linkLeadingIcon: [
                  'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)',
                  'transition-colors'
                ]
              }
            },
            {
              color: 'primary',
              variant: 'link',
              active: true,
              class: {
                link: 'text-(--ui-primary)',
                linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)'
              }
            },
            {
              color: 'neutral',
              variant: 'link',
              active: true,
              class: {
                link: 'text-(--ui-text-highlighted)',
                linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)'
              }
            },
            {
              highlightColor: 'primary',
              highlight: true,
              level: true,
              active: true,
              class: {
                link: 'after:bg-(--ui-primary)'
              }
            },
            {
              highlightColor: 'neutral',
              highlight: true,
              level: true,
              active: true,
              class: {
                link: 'after:bg-(--ui-bg-inverted)'
              }
            }
          ],
          defaultVariants: {
            color: 'primary',
            highlightColor: 'primary',
            variant: 'pill'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.