ProgressGroup New
Usage
Use the ProgressGroup component to display multiple values as segments of a single progress bar.
- System19%
- Apps6%
- Documents9%
- Multimedia33%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'System',
value: 24,
color: 'neutral',
icon: 'i-lucide-cog'
},
{
label: 'Apps',
value: 8,
color: 'error',
icon: 'i-lucide-app-window'
},
{
label: 'Documents',
value: 12,
color: 'warning',
icon: 'i-lucide-file'
},
{
label: 'Multimedia',
value: 42,
color: 'success',
icon: 'i-lucide-film'
}
])
</script>
<template>
<UProgressGroup :max="128" :items="items" class="w-96" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'System',
value: 24,
color: 'neutral',
icon: 'i-lucide-cog'
},
{
label: 'Apps',
value: 8,
color: 'error',
icon: 'i-lucide-app-window'
},
{
label: 'Documents',
value: 12,
color: 'warning',
icon: 'i-lucide-file'
},
{
label: 'Multimedia',
value: 42,
color: 'success',
icon: 'i-lucide-film'
}
])
</script>
<template>
<UProgressGroup :max="128" :items="items" class="w-96" />
</template>
Items
Use the items prop as an array of objects with the following properties:
label?: stringicon?: stringvalue?: numbercolor?: "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" | (string & {})slot?: stringclass?: anyui?: { segment?: ClassNameValue, indicator?: ClassNameValue, item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingDot?: ClassNameValue, itemLabel?: ClassNameValue, itemTrailing?: ClassNameValue }
- Compute42%
- Storage18%
- Bandwidth9%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Compute',
value: 42,
color: 'primary'
},
{
label: 'Storage',
value: 18,
color: 'info'
},
{
label: 'Bandwidth',
value: 9,
color: 'warning'
}
])
</script>
<template>
<UProgressGroup :items="items" class="w-96" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Compute',
value: 42,
color: 'primary'
},
{
label: 'Storage',
value: 18,
color: 'info'
},
{
label: 'Bandwidth',
value: 9,
color: 'warning'
}
])
</script>
<template>
<UProgressGroup :items="items" class="w-96" />
</template>
icon get a colored dot in the list instead.Max
Use the max prop to set the value all items add up to. Defaults to 100.
- Used25%
- Reserved13%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Used',
value: 128,
color: 'primary'
},
{
label: 'Reserved',
value: 64,
color: 'neutral'
}
])
</script>
<template>
<UProgressGroup :max="512" :items="items" class="w-96" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Used',
value: 128,
color: 'primary'
},
{
label: 'Reserved',
value: 64,
color: 'neutral'
}
])
</script>
<template>
<UProgressGroup :max="512" :items="items" class="w-96" />
</template>
0 and max, and segments that add up to more than max share the track proportionally.Status
Use the status prop to display the summed value above the bar.
- System19%
- Apps6%
- Multimedia33%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'System',
value: 24,
color: 'neutral'
},
{
label: 'Apps',
value: 8,
color: 'error'
},
{
label: 'Multimedia',
value: 42,
color: 'success'
}
])
</script>
<template>
<UProgressGroup status :max="128" :items="items" class="w-96" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'System',
value: 24,
color: 'neutral'
},
{
label: 'Apps',
value: 8,
color: 'error'
},
{
label: 'Multimedia',
value: 42,
color: 'success'
}
])
</script>
<template>
<UProgressGroup status :max="128" :items="items" class="w-96" />
</template>
:ui="{ status: 'w-full' }" to make it span the full width instead.Color
Use the color prop to change the color of every segment that doesn't set its own.
- Read42%
- Write18%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Read',
value: 42
},
{
label: 'Write',
value: 18
}
])
</script>
<template>
<UProgressGroup color="neutral" :items="items" class="w-96" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Read',
value: 42
},
{
label: 'Write',
value: 18
}
])
</script>
<template>
<UProgressGroup color="neutral" :items="items" class="w-96" />
</template>
color accept any CSS color value, which is handy for palettes outside the theme.Size
Use the size prop to change the size of the ProgressGroup.
- Read42%
- Write18%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Read',
value: 42,
color: 'primary'
},
{
label: 'Write',
value: 18,
color: 'info'
}
])
</script>
<template>
<UProgressGroup size="xl" :items="items" class="w-96" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Read',
value: 42,
color: 'primary'
},
{
label: 'Write',
value: 18,
color: 'info'
}
])
</script>
<template>
<UProgressGroup size="xl" :items="items" class="w-96" />
</template>
Orientation
Use the orientation prop to change the orientation of the ProgressGroup. Defaults to horizontal.
- Read42%
- Write18%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Read',
value: 42,
color: 'primary'
},
{
label: 'Write',
value: 18,
color: 'info'
}
])
</script>
<template>
<UProgressGroup orientation="vertical" :items="items" class="h-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ProgressGroupItem } from '@nuxt/ui'
const items = ref<ProgressGroupItem[]>([
{
label: 'Read',
value: 42,
color: 'primary'
},
{
label: 'Write',
value: 18,
color: 'info'
}
])
</script>
<template>
<UProgressGroup orientation="vertical" :items="items" class="h-48" />
</template>
Examples
With status slot
Use the #status slot to replace the summed percentage with your own content.
86GB used
42GB remaining
- System19%
- Apps6%
- Documents9%
- Multimedia33%
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const max = 128
const items: ProgressGroupItem[] = [
{ label: 'System', value: 24, color: 'neutral', icon: 'i-lucide-cog' },
{ label: 'Apps', value: 8, color: 'error', icon: 'i-lucide-app-window' },
{ label: 'Documents', value: 12, color: 'warning', icon: 'i-lucide-file' },
{ label: 'Multimedia', value: 42, color: 'success', icon: 'i-lucide-film' }
]
const used = items.reduce((total, item) => total + (item.value ?? 0), 0)
</script>
<template>
<UProgressGroup :items="items" :max="max" status class="w-96" :ui="{ status: 'w-full justify-between' }">
<template #status>
<p>{{ used }}GB used</p>
<p class="text-muted">
{{ max - used }}GB remaining
</p>
</template>
</UProgressGroup>
</template>
With item slots
Use the #item-label and #item-trailing slots to change what each entry displays. Both receive the item, its index and its percent.
- System24GB
- Apps8GB
- Documents12GB
- Multimedia42GB
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const items: ProgressGroupItem[] = [
{ label: 'System', value: 24, color: 'neutral' },
{ label: 'Apps', value: 8, color: 'error' },
{ label: 'Documents', value: 12, color: 'warning' },
{ label: 'Multimedia', value: 42, color: 'success' }
]
</script>
<template>
<UProgressGroup :items="items" :max="128" class="w-96">
<template #item-label="{ item }">
<span class="font-medium">{{ item.label }}</span>
</template>
<template #item-trailing="{ item }">
{{ item.value }}GB
</template>
</UProgressGroup>
</template>
With custom colors
Give each item a CSS color to build a breakdown outside the theme palette.
70% Full
~89.7K / 128K Tokens
- System prompt4.2K
- Tool definitions18.4K
- Rules12.8K
- Skills7.1K
- MCP & dynamic tools17.1K
- Subagent definitions5.5K
- Conversation24.6K
<script setup lang="ts">
import type { ProgressGroupItem } from '@nuxt/ui'
const max = 128
const items: ProgressGroupItem[] = [
{ label: 'System prompt', value: 4.2, color: 'var(--color-neutral-400)' },
{ label: 'Tool definitions', value: 18.4, color: 'var(--color-violet-400)' },
{ label: 'Rules', value: 12.8, color: 'var(--color-green-400)' },
{ label: 'Skills', value: 7.1, color: 'var(--color-amber-400)' },
{ label: 'MCP & dynamic tools', value: 17.1, color: 'var(--color-rose-400)' },
{ label: 'Subagent definitions', value: 5.5, color: 'var(--color-sky-400)' },
{ label: 'Conversation', value: 24.6, color: 'var(--color-orange-400)' }
]
const used = items.reduce((total, item) => total + (item.value ?? 0), 0)
</script>
<template>
<UProgressGroup :items="items" :max="max" status class="w-96" :ui="{ status: 'w-full justify-between' }">
<template #status="{ percent }">
<p>{{ percent }}% Full</p>
<p class="text-muted">
~{{ used.toFixed(1) }}K / {{ max }}K Tokens
</p>
</template>
<template #item-trailing="{ item }">
{{ item.value }}K
</template>
</UProgressGroup>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
items | T[] | |
max | 100 | numberThe value all items add up to, used to compute each segment's share of the track. |
status | boolean Display the summed progress value. | |
size | 'md' | "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" |
color | 'primary' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" | stringAny theme color, or any CSS color value for palettes outside the theme. |
orientation | 'horizontal' | "horizontal" | "vertical"The orientation of the progress bar. |
ui | { root?: SlotClass; base?: SlotClass; segment?: SlotClass; indicator?: SlotClass; status?: SlotClass; list?: SlotClass; item?: SlotClass; itemLeadingIcon?: SlotClass; itemLeadingDot?: SlotClass; itemLabel?: SlotClass; itemTrailing?: SlotClass; } |
Slots
| Slot | Type |
|---|---|
status | { percent: number; } |
item | { item: T; index: number; percent: number; } |
item-leading | { item: T; index: number; percent: number; } |
item-label | { item: T; index: number; percent: number; } |
item-trailing | { item: T; index: number; percent: number; } |
Theme
export default defineAppConfig({
ui: {
progressGroup: {
slots: {
root: 'gap-2',
base: 'flex overflow-hidden rounded-full bg-accented',
segment: 'duration-200 ease-out motion-reduce:transition-none',
indicator: 'size-full',
status: 'flex text-dimmed duration-200 ease-out motion-reduce:transition-none',
list: 'flex flex-col gap-1',
item: 'flex items-center gap-1.5 min-w-0',
itemLeadingIcon: 'shrink-0',
itemLeadingDot: 'shrink-0 rounded-full',
itemLabel: 'truncate',
itemTrailing: 'ms-auto shrink-0 text-dimmed'
},
variants: {
color: {
primary: {
indicator: 'bg-primary',
itemLeadingIcon: 'text-primary',
itemLeadingDot: 'bg-primary'
},
secondary: {
indicator: 'bg-secondary',
itemLeadingIcon: 'text-secondary',
itemLeadingDot: 'bg-secondary'
},
success: {
indicator: 'bg-success',
itemLeadingIcon: 'text-success',
itemLeadingDot: 'bg-success'
},
info: {
indicator: 'bg-info',
itemLeadingIcon: 'text-info',
itemLeadingDot: 'bg-info'
},
warning: {
indicator: 'bg-warning',
itemLeadingIcon: 'text-warning',
itemLeadingDot: 'bg-warning'
},
error: {
indicator: 'bg-error',
itemLeadingIcon: 'text-error',
itemLeadingDot: 'bg-error'
},
neutral: {
indicator: 'bg-inverted',
itemLeadingIcon: 'text-highlighted',
itemLeadingDot: 'bg-inverted'
}
},
size: {
'2xs': {
status: 'text-xs',
list: 'text-xs',
itemLeadingIcon: 'size-3',
itemLeadingDot: 'size-1.5'
},
xs: {
status: 'text-xs',
list: 'text-xs',
itemLeadingIcon: 'size-3',
itemLeadingDot: 'size-1.5'
},
sm: {
status: 'text-sm',
list: 'text-sm',
itemLeadingIcon: 'size-4',
itemLeadingDot: 'size-2'
},
md: {
status: 'text-sm',
list: 'text-sm',
itemLeadingIcon: 'size-4',
itemLeadingDot: 'size-2'
},
lg: {
status: 'text-sm',
list: 'text-sm',
itemLeadingIcon: 'size-4',
itemLeadingDot: 'size-2'
},
xl: {
status: 'text-base',
list: 'text-base',
itemLeadingIcon: 'size-5',
itemLeadingDot: 'size-2.5'
},
'2xl': {
status: 'text-base',
list: 'text-base',
itemLeadingIcon: 'size-5',
itemLeadingDot: 'size-2.5'
}
},
orientation: {
horizontal: {
root: 'w-full flex flex-col',
base: 'w-full flex-row',
segment: 'h-full transition-[width]',
status: 'flex-row items-center justify-end w-(--percent) min-w-fit transition-[width]'
},
vertical: {
root: 'h-full flex flex-row',
base: 'h-full flex-col',
segment: 'w-full transition-[height]',
status: 'flex-col justify-end h-(--percent) min-h-fit transition-[height]'
}
}
},
compoundVariants: [
{
orientation: 'horizontal',
size: '2xs',
class: 'h-px'
},
{
orientation: 'horizontal',
size: 'xs',
class: 'h-0.5'
},
{
orientation: 'horizontal',
size: 'sm',
class: 'h-1'
},
{
orientation: 'horizontal',
size: 'md',
class: 'h-2'
},
{
orientation: 'horizontal',
size: 'lg',
class: 'h-3'
},
{
orientation: 'horizontal',
size: 'xl',
class: 'h-4'
},
{
orientation: 'horizontal',
size: '2xl',
class: 'h-5'
},
{
orientation: 'vertical',
size: '2xs',
class: 'w-px'
},
{
orientation: 'vertical',
size: 'xs',
class: 'w-0.5'
},
{
orientation: 'vertical',
size: 'sm',
class: 'w-1'
},
{
orientation: 'vertical',
size: 'md',
class: 'w-2'
},
{
orientation: 'vertical',
size: 'lg',
class: 'w-3'
},
{
orientation: 'vertical',
size: 'xl',
class: 'w-4'
},
{
orientation: 'vertical',
size: '2xl',
class: 'w-5'
}
],
defaultVariants: {
color: 'primary',
size: 'md'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
progressGroup: {
slots: {
root: 'gap-2',
base: 'flex overflow-hidden rounded-full bg-accented',
segment: 'duration-200 ease-out motion-reduce:transition-none',
indicator: 'size-full',
status: 'flex text-dimmed duration-200 ease-out motion-reduce:transition-none',
list: 'flex flex-col gap-1',
item: 'flex items-center gap-1.5 min-w-0',
itemLeadingIcon: 'shrink-0',
itemLeadingDot: 'shrink-0 rounded-full',
itemLabel: 'truncate',
itemTrailing: 'ms-auto shrink-0 text-dimmed'
},
variants: {
color: {
primary: {
indicator: 'bg-primary',
itemLeadingIcon: 'text-primary',
itemLeadingDot: 'bg-primary'
},
secondary: {
indicator: 'bg-secondary',
itemLeadingIcon: 'text-secondary',
itemLeadingDot: 'bg-secondary'
},
success: {
indicator: 'bg-success',
itemLeadingIcon: 'text-success',
itemLeadingDot: 'bg-success'
},
info: {
indicator: 'bg-info',
itemLeadingIcon: 'text-info',
itemLeadingDot: 'bg-info'
},
warning: {
indicator: 'bg-warning',
itemLeadingIcon: 'text-warning',
itemLeadingDot: 'bg-warning'
},
error: {
indicator: 'bg-error',
itemLeadingIcon: 'text-error',
itemLeadingDot: 'bg-error'
},
neutral: {
indicator: 'bg-inverted',
itemLeadingIcon: 'text-highlighted',
itemLeadingDot: 'bg-inverted'
}
},
size: {
'2xs': {
status: 'text-xs',
list: 'text-xs',
itemLeadingIcon: 'size-3',
itemLeadingDot: 'size-1.5'
},
xs: {
status: 'text-xs',
list: 'text-xs',
itemLeadingIcon: 'size-3',
itemLeadingDot: 'size-1.5'
},
sm: {
status: 'text-sm',
list: 'text-sm',
itemLeadingIcon: 'size-4',
itemLeadingDot: 'size-2'
},
md: {
status: 'text-sm',
list: 'text-sm',
itemLeadingIcon: 'size-4',
itemLeadingDot: 'size-2'
},
lg: {
status: 'text-sm',
list: 'text-sm',
itemLeadingIcon: 'size-4',
itemLeadingDot: 'size-2'
},
xl: {
status: 'text-base',
list: 'text-base',
itemLeadingIcon: 'size-5',
itemLeadingDot: 'size-2.5'
},
'2xl': {
status: 'text-base',
list: 'text-base',
itemLeadingIcon: 'size-5',
itemLeadingDot: 'size-2.5'
}
},
orientation: {
horizontal: {
root: 'w-full flex flex-col',
base: 'w-full flex-row',
segment: 'h-full transition-[width]',
status: 'flex-row items-center justify-end w-(--percent) min-w-fit transition-[width]'
},
vertical: {
root: 'h-full flex flex-row',
base: 'h-full flex-col',
segment: 'w-full transition-[height]',
status: 'flex-col justify-end h-(--percent) min-h-fit transition-[height]'
}
}
},
compoundVariants: [
{
orientation: 'horizontal',
size: '2xs',
class: 'h-px'
},
{
orientation: 'horizontal',
size: 'xs',
class: 'h-0.5'
},
{
orientation: 'horizontal',
size: 'sm',
class: 'h-1'
},
{
orientation: 'horizontal',
size: 'md',
class: 'h-2'
},
{
orientation: 'horizontal',
size: 'lg',
class: 'h-3'
},
{
orientation: 'horizontal',
size: 'xl',
class: 'h-4'
},
{
orientation: 'horizontal',
size: '2xl',
class: 'h-5'
},
{
orientation: 'vertical',
size: '2xs',
class: 'w-px'
},
{
orientation: 'vertical',
size: 'xs',
class: 'w-0.5'
},
{
orientation: 'vertical',
size: 'sm',
class: 'w-1'
},
{
orientation: 'vertical',
size: 'md',
class: 'w-2'
},
{
orientation: 'vertical',
size: 'lg',
class: 'w-3'
},
{
orientation: 'vertical',
size: 'xl',
class: 'w-4'
},
{
orientation: 'vertical',
size: '2xl',
class: 'w-5'
}
],
defaultVariants: {
color: 'primary',
size: 'md'
}
}
}
})
]
})