ContentNavigation
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 />
</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:
<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:
<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 |
---|---|---|
as |
|
The element or component this component should render as. |
defaultOpen |
|
When |
trailingIcon |
|
The icon displayed to toggle the accordion. |
color |
|
|
variant |
|
|
highlight |
|
Display a line next to the active link. |
highlightColor |
|
|
collapsible |
|
When type is "single", allows closing content when clicking trigger for an open item. When type is "multiple", this prop has no effect. |
level |
|
|
navigation |
| |
type |
|
Determines whether a "single" or "multiple" items can be selected at a time. This prop will overwrite the inferred type from |
disabled |
|
When |
ui |
|
Slots
Slot | Type |
---|---|
link |
|
link-leading |
|
link-title |
|
link-trailing |
|
Emits
Event | Type |
---|---|
update:modelValue |
|
Theme
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: 'ps-1.5 -ms-px',
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: {}
}
},
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'
}
}
}
})
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: 'ps-1.5 -ms-px',
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: {}
}
},
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'
}
}
}
})
]
})