๐ŸŸข Breadcrumb


Basic

NBreadcrumb - use to display the path of the current page and the hierarchy of previous routes/links.

PropTypeDefaultDescription
itemsArray[]The array of Links that wrapped around NButton component, which means that all the props and slots of NButton are available to use.
<script setup lang="ts">
const items = ref([
  {
    label: 'i-radix-icons-home',
    icon: true,
    to: '/',
  },
  {
    label: 'Components',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    to: '/components/breadcrumb',
  },
])

const items2 = ref([
  {
    leading: 'i-radix-icons-home',
    label: 'Home',
    to: '/',
  },
  {
    label: 'Components',
    leading: 'i-radix-icons-cube',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    leading: 'i-radix-icons-link-2',
    to: '/components/breadcrumb',
  },
])
</script>

<template>
  <div class="flex flex-col space-y-4">
    <NBreadcrumb
      :items
    />

    <NSeparator />

    <NBreadcrumb
      :items="items2"
    />
  </div>
</template>

Variant & Color

PropTypeDefaultDescription
breadcrumb-active{variant}-{color}text-primaryThe active breadcrumb variant and color.
breadcrumb-inactive{variant}-{color}text-mutedThe inactive breadcrumb variant and color.
You can use any variant and colors provided by the NButton component, just make sure to use breadcrumb-active and breadcrumb-inactive as a prefix instead of btn.
<script setup lang="ts">
const items = ref([
  {
    label: 'i-radix-icons-home',
    icon: true,
    to: '/',
  },
  {
    label: 'Components',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    to: '/components/breadcrumb',
  },
])

const items2 = ref([
  {
    leading: 'i-radix-icons-home',
    label: 'Home',
    to: '/',
  },
  {
    label: 'Components',
    leading: 'i-radix-icons-cube',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    leading: 'i-radix-icons-link-2',
    to: '/components/breadcrumb',
  },
])
</script>

<template>
  <div class="flex flex-col space-y-4">
    <NBreadcrumb
      breadcrumb-active="text-black"
      :items
    />

    <NSeparator label="or" />

    <NBreadcrumb
      breadcrumb-active="text-orange"
      breadcrumb-inactive="text-red"
      :items
    />

    <NSeparator label="or" />

    <NBreadcrumb
      breadcrumb-active="solid-primary"
      breadcrumb-inactive="ghost-primary"
      :_breadcrumb-link="{
        size: 'xs',
        class: 'rounded-full btn-rectangle',
      }"
      :items="items2"
    />
  </div>
</template>

Separator

PropTypeDefaultDescription
separatorStringi-radix-icons-chevron-rightThe separator icon.
You can use any icon provided by the NIcon component, the default is i-radix-icons-chevron-right.
<script setup lang="ts">
const items = ref([
  {
    label: 'i-radix-icons-home',
    icon: true,
    to: '/',
  },
  {
    label: 'Components',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    to: '/components/breadcrumb',
  },
])

const items2 = ref([
  {
    leading: 'i-radix-icons-home',
    label: 'Home',
    to: '/',
  },
  {
    label: 'Components',
    leading: 'i-radix-icons-cube',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    leading: 'i-radix-icons-link-2',
    to: '/components/breadcrumb',
  },
])
</script>

<template>
  <div class="flex flex-col space-y-4">
    <NBreadcrumb
      :items
      separator="i-lucide-slash"
    />

    <NSeparator />

    <NBreadcrumb
      :items="items2"
      separator="i-radix-icons-border-dashed"
    />
  </div>
</template>

Size

PropTypeDefaultDescription
sizeStringsmThe size of the breadcrumb. This will be applied to all the breadcrumb components.
_breadcrumbLink.sizeStringsmThe size of the breadcrumb link.
_breadcrumbSeparator.sizeStringsmThe size of the breadcrumb separator.

๐Ÿš€ You can freely adjust the size of the breadcrumb using any size imaginable. No limits exist, and you can use breakpoints such as sm:sm, xs:lg to change size based on screen size or states such as hover:lg, focus:3xl to change size based on input state and more.

<script setup lang="ts">
const items = ref([
  {
    label: 'i-radix-icons-home',
    icon: true,
    to: '/',
  },
  {
    label: 'Components',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    to: '/components/breadcrumb',
  },
])

const items2 = ref([
  {
    leading: 'i-radix-icons-home',
    label: 'Home',
    to: '/',
  },
  {
    label: 'Components',
    leading: 'i-radix-icons-cube',
    to: '/components/accordion',
  },
  {
    label: 'Breadcrumb',
    leading: 'i-radix-icons-link-2',
    to: '/components/breadcrumb',
  },
])
</script>

<template>
  <div class="flex flex-col space-y-4">
    <NBreadcrumb
      :items
      size="lg"
    />

    <NSeparator label="or" />

    <NBreadcrumb
      :items="items2"
      separator="i-lucide-slash"
      :_breadcrumb-link="{
        size: 'xs',
      }"
      :_breadcrumb-separator="{
        size: 'lg',
      }"
    />
  </div>
</template>

Slots

You can use the following slots to customize the breadcrumb.

NameDescriptionProps
defaultThe breadcrumb itemitem
separatorThe separatoritem
rootThe root breadcrumbitems
listThe list of itemsitem
<script setup lang="ts">
const items = ref([
  { label: 'Components', to: '/components/accordion' },
  { label: 'Alert', to: '/components/alert' },
  { label: 'Breadcrumb', to: '/components/breadcrumb' },
])

const colors = ['#ffd45e', '#45ad2d', '#3b54c4', '#b156db', '#a6a2a3', '#ff7a97', '#a1612a'] as const

function getRandomColor() {
  const index = Math.floor(Math.random() * colors.length)
  return colors[index]
}
</script>

<template>
  <NBreadcrumb :items>
    <template #separator>
      <NSeparator orientation="vertical" class="h-10" />
    </template>

    <template #default="{ item }">
      <div class="flex items-center gap2">
        <div
          :style="{ backgroundColor: getRandomColor() }"
          class="h-2 w-2 rounded-full"
        />
        {{ item.label }}
      </div>
    </template>
  </NBreadcrumb>
</template>

Props

import type { HTMLAttributes } from 'vue'
import type { NButtonProps } from './button'

interface BaseExtensions { class?: HTMLAttributes['class'] }

export interface NBreadcrumbProps extends BaseExtensions, Pick<NBreadcrumbLinkProps, 'breadcrumbActive' | 'breadcrumbInactive'> {
  /**
   * List of items to display in the breadcrumb.
   *
   * @example
   * items: [{ label: 'Home', to: '/' }, { label: 'About', to: '/about' }]
   */
  items: Partial<NButtonProps>[]
  separator?: NBreadcrumbSeparatorProps['icon']
  ellipsis?: NBreadcrumbEllipsisProps['icon']
  /**
   * Allows you to change the size of the input.
   *
   * @default sm
   *
   * @example
   * size="sm" | size="2cm" | size="2rem" | size="2px"
   */
  size?: string

  // maxItems?: number

  // sub-components
  _breadcrumbSeparator?: Partial<NBreadcrumbSeparatorProps>
  _breadcrumbItem?: Partial<NBreadcrumbItemProps>
  _breadcrumbRoot?: Partial<NBreadcrumbRootProps>
  _breadcrumbList?: Partial<NBreadcrumbListProps>
  _breadcrumbLink?: Partial<NBreadcrumbLinkProps>
  _breadcrumbEllipsis?: Partial<NBreadcrumbEllipsisProps>

  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/breadcrumb.ts
   */
  una?: NBreadcrumbUnaProps
}

export interface NBreadcrumbRootProps extends BaseExtensions {
  una?: Pick<NBreadcrumbUnaProps, 'breadcrumbRoot'>
}

export interface NBreadcrumbItemProps extends BaseExtensions {
  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/breadcrumb.ts
   */
  una?: Pick<NBreadcrumbUnaProps, 'breadcrumbItem'>
}

export interface NBreadcrumbSeparatorProps extends BaseExtensions {
  /**
   * Custom separator icon.
   */
  icon?: string
  /**
   * Allows you to change the size of the input.
   *
   * @default sm
   *
   * @example
   * size="sm" | size="2cm" | size="2rem" | size="2px"
   */
  size?: string
  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/breadcrumb.ts
   */
  una?: Pick<NBreadcrumbUnaProps, 'breadcrumbSeparator' | 'breadcrumbSeparatorIcon'>
}

export interface NBreadcrumbListProps extends BaseExtensions {
  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/breadcrumb.ts
   */
  una?: Pick<NBreadcrumbUnaProps, 'breadcrumbList'>
}

export interface NBreadcrumbLinkProps extends BaseExtensions, NButtonProps {
  active?: boolean
  breadcrumbActive?: string
  breadcrumbInactive?: string
}

export interface NBreadcrumbEllipsisProps extends BaseExtensions {
  /**
   * Custom separator icon.
   */
  icon?: string
  /**
   *  /**
   * Allows you to change the size of the input.
   *
   * @default sm
   *
   * @example
   * size="sm" | size="2cm" | size="2rem" | size="2px"
   */
  size?: string
  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/breadcrumb.ts
   */
  una?: Pick<NBreadcrumbUnaProps, 'breadcrumbEllipsis' | 'breadcrumbEllipsisIcon'>
}

interface NBreadcrumbUnaProps {
  breadcrumb?: HTMLAttributes['class']
  breadcrumbRoot?: HTMLAttributes['class']
  breadcrumbItem?: HTMLAttributes['class']
  breadcrumbEllipsis?: HTMLAttributes
  breadcrumbEllipsisIcon?: HTMLAttributes['class']
  breadcrumbList?: HTMLAttributes['class']
  breadcrumbSeparator?: HTMLAttributes['class']
  breadcrumbSeparatorIcon?: HTMLAttributes['class']
}

Presets

type BreadcrumbPrefix = 'breadcrumb'

export const staticBreadcrumb: Record<`${BreadcrumbPrefix}-${string}` | BreadcrumbPrefix, string> = {
  // config
  'breadcrumb': '',
  'breadcrumb-active': 'breadcrumb-active-text-primary',
  'breadcrumb-inactive': 'breadcrumb-inactive-text-muted',
  'breadcrumb-separator-icon': 'i-radix-icons-chevron-right',
  'breadcrumb-elipsis-icon': 'i-radix-icons-dots-horizontal',

  // components
  'breadcrumb-root': '',
  'breadcrumb-list': 'flex flex-wrap items-center break-words text-muted',
  'breadcrumb-link': 'transition-colors font-normal',
  'breadcrumb-item': 'inline-flex items-center gap-1.5',

  // TODO
  'breadcrumb-ellipsis': 'flex items-center justify-center',
}

export const dynamicBreadcrumb = [
  // states
  [
    /^breadcrumb-active-([^-]+)-([^-]+)$/,
    ([, variant = 'text', color = 'primary']) =>
      `data-[state=active]:btn-${variant}-${color}`,
  ],
  [
    /^breadcrumb-inactive-([^-]+)-([^-]+)$/,
    ([, variant = 'text', color = 'muted']) =>
      `data-[state=inactive]:btn-${variant}-${color}`,
  ],
]

export const breadcrumb = [
  ...dynamicBreadcrumb,
  staticBreadcrumb,
]

Component

<script lang="ts" setup>
import type { NBreadcrumbProps } from '../../../types'
import { cn } from '../../../utils'
import BreadcrumbItem from './BreadcrumbItem.vue'
import BreadcrumbLink from './BreadcrumbLink.vue'
import BreadcrumbList from './BreadcrumbList.vue'
import BreadcrumbRoot from './BreadcrumbRoot.vue'
import BreadcrumbSeparator from './BreadcrumbSeparator.vue'

const props = defineProps<NBreadcrumbProps>()
</script>

<template>
  <BreadcrumbRoot
    :class="cn(
      'breadcrumb',
      props.class,
      props.una?.breadcrumb,
    )"
    :una
    :size
    v-bind="_breadcrumbRoot"
  >
    <slot name="root" :items="items">
      <BreadcrumbList
        :una
        :size
        v-bind="_breadcrumbList"
      >
        <template
          v-for="(item, i) in props.items"
          :key="i"
        >
          <slot name="list" :item="item">
            <BreadcrumbItem
              :una
              :size
              v-bind="_breadcrumbItem"
            >
              <slot name="item" :item="item">
                <BreadcrumbLink
                  :active="i === items.length - 1"
                  :breadcrumb-active="props.breadcrumbActive"
                  :breadcrumb-inactive="props.breadcrumbInactive"
                  :size
                  v-bind="{
                    ...item,
                    ..._breadcrumbLink,
                  }"
                >
                  <slot :item="item" />
                </BreadcrumbLink>
              </slot>
            </BreadcrumbItem>
            <BreadcrumbSeparator
              v-if="i < props.items!.length - 1"
              :icon="props.separator"
              :size
              :una
              v-bind="_breadcrumbSeparator"
            >
              <slot name="separator" />
            </BreadcrumbSeparator>
          </slot>
        </template>
      </BreadcrumbList>
    </slot>
  </BreadcrumbRoot>
</template>
<script lang="ts" setup>
import type { NBreadcrumbRootProps } from '../../../types'
import { cn } from '../../../utils'

const props = defineProps<NBreadcrumbRootProps>()
</script>

<template>
  <nav
    aria-label="breadcrumb"
    :class="cn(
      props.class,
      props.una?.breadcrumbRoot,
    )"
  >
    <slot />
  </nav>
</template>
<script lang="ts" setup>
import type { NBreadcrumbListProps } from '../../../types'
import { cn } from '../../../utils'

const props = defineProps<NBreadcrumbListProps>()
</script>

<template>
  <ol
    :class="cn(
      'breadcrumb-list',
      props.class,
      props.una?.breadcrumbList,
    )"
  >
    <slot />
  </ol>
</template>
<script lang="ts" setup>
import type { NBreadcrumbLinkProps } from '../../../types'
import { computed } from 'vue'
import { cn } from '../../../utils'
import Button from '../../elements/Button.vue'

const props = withDefaults(defineProps<NBreadcrumbLinkProps>(), {
  active: false,
  breadcrumbActive: '~',
  breadcrumbInactive: '~',
  size: 'sm',
})

const activeAttrs = computed(() => {
  if (!props.active) {
    return {
      'data-state': 'inactive',
    }
  }

  return {
    'aria-disabled': true,
    'tabindex': -1,
    'data-state': 'active',
    'aria-current': 'page',
  }
})
</script>

<template>
  <Button
    v-bind="{ ...props, ...activeAttrs }"
    :class="cn(
      'breadcrumb-link',
      { 'cursor-text': active },
    )"
    :to="disabled ? undefined : props.to"
  >
    <slot />
  </Button>
</template>
<script lang="ts" setup>
import type { NBreadcrumbItemProps } from '../../../types'
import { cn } from '../../../utils'

const props = defineProps<NBreadcrumbItemProps>()
</script>

<template>
  <li
    :class="cn(
      'breadcrumb-item',
      props.class,
      props.una?.breadcrumbItem,
    )"
  >
    <slot />
  </li>
</template>
<script lang="ts" setup>
import type { NBreadcrumbSeparatorProps } from '../../../types'
import { cn } from '../../../utils'
import Icon from '../../elements/Icon.vue'

const props = withDefaults(defineProps<NBreadcrumbSeparatorProps>(), {
  icon: 'breadcrumb-separator-icon',
  size: 'sm',
})
</script>

<template>
  <li
    role="presentation"
    aria-hidden="true"
    :class="cn(
      '',
      props.class,
      props.una?.breadcrumbSeparator,
    )"
    :size
  >
    <slot>
      <Icon
        :name="icon"
        :class="props.una?.breadcrumbSeparatorIcon"
      />
    </slot>
  </li>
</template>
<script lang="ts" setup>
import type { NBreadcrumbEllipsisProps } from '../../../types'
import { cn } from '../../../utils'

const props = withDefaults(defineProps<NBreadcrumbEllipsisProps>(), {
  icon: 'breadcrumb-ellipsis-icon',
  size: 'sm',
  square: 9,
})
</script>

<template>
  <span
    role="presentation"
    aria-hidden="true"
    :class="cn(
      'breadcrumb-ellipsis',
      props.class,
      props.una?.breadcrumbEllipsis,
    )"
    :size
  >
    <slot>
      <NIcon
        :name="icon"
        :class="props.una?.breadcrumbEllipsisIcon"
        aria-hidden="true"
      />
    </slot>
    <span class="sr-only">More</span>
  </span>
</template>