Use the StatCard component to display key metrics with an icon, title, value, and optional trend indicator.
<template>
<UStatCard icon="i-lucide-users" title="Total Users" value="12,345" :trend="12.5" />
</template>
The trend-direction is automatically calculated based on the trend value. Positive values show a green up arrow, negative values show a red down arrow.
<template>
<UStatCard icon="i-lucide-shopping-cart" title="Sales" value="$45,231" :trend="-8.2" />
</template>
You can explicitly set trend-direction to override the automatic calculation. This is useful when you want to show a positive number as "down" or vice versa.
<template>
<UStatCard
icon="i-lucide-dollar-sign"
title="Expenses"
value="$12,500"
:trend="15.3"
trend-direction="down"
/>
</template>
You can omit the trend prop to hide the trend indicator.
<template>
<UStatCard icon="i-lucide-dollar-sign" title="Revenue" value="$125,430" />
</template>
Use the icon prop to set the icon displayed in the card.
<template>
<UStatCard icon="i-lucide-activity" title="Active Sessions" value="1,234" :trend="15.3" />
</template>
Use the color prop to change the color of the icon and trend indicator.
<template>
<UStatCard
color="success"
icon="i-lucide-check-circle"
title="Completed"
value="98.5%"
:trend="2.1"
/>
</template>
<template>
<UStatCard
color="warning"
icon="i-lucide-alert-triangle"
title="Pending"
value="23"
:trend="5.4"
/>
</template>
<template>
<UStatCard
color="error"
icon="i-lucide-x-circle"
title="Failed"
value="12"
:trend="3.2"
trend-direction="down"
/>
</template>
Use the current and max props to display a progress bar. The show-label prop controls whether to show the "current / max" label.
<script setup lang="ts">
const current = ref(8000)
</script>
<template>
<UStatCard icon="i-lucide-dollar-sign" title="Monthly Budget" :current="8000" :max="10000" class="max-w-xl" />
</template>
Without label:
<script setup lang="ts">
const current = ref(65)
</script>
<template>
<UStatCard icon="i-lucide-hard-drive" title="Storage Used" :current="65" :max="100" :show-label="false" class="max-w-xl" />
</template>
With trend:
<script setup lang="ts">
const current = ref(7500)
</script>
<template>
<UStatCard icon="i-lucide-target" title="Sales Target" :current="7500" :max="10000" :trend="12.5" class="max-w-xl" />
</template>
Use the data prop to display a sparkline chart. The show-area prop controls whether to show the area under the line.
<template>
<UStatCard icon="i-lucide-trending-up" title="Sales Trend" value="$45,231" :data="[
20,
-35,
30,
45,
50,
40,
55,
60,
55,
65,
70,
75
]" />
</template>
With area:
<template>
<UStatCard icon="i-lucide-bar-chart" title="Growth" value="24.5%" :data="[
15,
20,
25,
30,
35,
40,
45,
50,
55,
60,
65,
70
]" show-area />
</template>
With trend:
<template>
<UStatCard icon="i-lucide-line-chart" title="Performance" value="98.5%" :data="[
85,
90,
88,
92,
95,
97,
98
]" :trend="12.5" show-area />
</template>
Use the size prop to change the size of the stat card.
<template>
<UStatCard size="xs" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
<template>
<UStatCard size="sm" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
<template>
<UStatCard size="md" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
<template>
<UStatCard size="lg" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
<template>
<UStatCard size="xl" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
Use the variant prop to change the visual style of the card.
<template>
<UStatCard variant="solid" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
<template>
<UStatCard variant="soft" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
You can customize any part of the stat card using slots.
<template>
<UStatCard icon="i-lucide-trending-up" title="Growth" value="24.5%">
<template #trend> vs last month </template>
</UStatCard>
</template>
StatCard components work great in a grid layout for dashboards.
<script setup lang="ts">
const class = ref('grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 max-w-6xl')
</script>
<template>
<UStatCard />
</template>
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
icon | anyThe icon to display. | |
title | stringThe title/label of the stat. | |
value | string | numberThe main value to display. | |
trend | numberThe trend percentage value (e.g., 12.5 for +12.5%). | |
trendDirection | "up" | "down"The direction of the trend. If not provided, it will be automatically calculated based on the trend value (negative = 'down', positive/zero = 'up'). | |
current | numberThe current progress value (for progress bar). | |
max | 100 | numberThe maximum progress value (for progress bar). |
showLabel | true | boolean Show the "current / max" label when progress bar is displayed. |
progressColor | "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"Color for the progress bar (inherits color by default). | |
data | number[]Array of numbers for the sparkline chart. | |
strokeWidth | 2 | numberStroke width of the sparkline. |
showArea | false | boolean Show area under the sparkline. |
height | numberHeight of the sparkline chart. | |
color | 'primary' | "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" |
size | 'md' | "md" | "xs" | "sm" | "lg" | "xl" |
variant | 'outline' | "outline" | "solid" | "soft" | "subtle" | "ghost" | "naked" |
ui | { root?: ClassNameValue; header?: ClassNameValue; icon?: ClassNameValue; iconIcon?: ClassNameValue; content?: ClassNameValue; title?: ClassNameValue; value?: ClassNameValue; label?: ClassNameValue; progress?: ClassNameValue; sparkline?: ClassNameValue; sparklineSvg?: ClassNameValue; sparklinePath?: ClassNameValue; sparklineArea?: ClassNameValue; sparklineZeroLine?: ClassNameValue; trend?: ClassNameValue; trendIcon?: ClassNameValue; trendValue?: ClassNameValue; } |
| Slot | Type |
|---|---|
icon | { ui: object; } |
title | { title?: string | undefined; ui: object; } |
value | { value?: string | number | undefined; ui: object; } |
label | { current: number; max: number; ui: object; } |
progress | { current: number; max: number; percent: number; ui: object; } |
sparkline | { path: string; areaPath: string; viewBox: string; zeroLine: string | null; ui: object; } |
trend | { trend?: number | undefined; trendDirection?: "up" | "down" | undefined; ui: object; } |
default | { ui: object; } |
export default defineAppConfig({
ui: {
statCard: {
slots: {
root: 'rounded-lg overflow-hidden flex flex-col justify-between',
header: 'flex items-start gap-4 p-4 sm:p-6',
icon: 'flex items-center justify-center shrink-0 rounded-lg',
iconIcon: '',
content: 'flex-1 min-w-0 ',
title: 'text-sm font-medium text-muted',
value: 'text-2xl font-semibold text-default mt-1',
label: 'text-xs text-muted mt-1',
progress: '',
sparkline: '',
sparklineSvg: 'w-full',
sparklinePath: 'fill-none transition-all duration-300',
sparklineArea: 'transition-opacity duration-300',
sparklineZeroLine: 'stroke-default dark:stroke-default',
trend: 'flex items-center gap-1 text-xs font-medium mt-1',
trendIcon: 'shrink-0',
trendValue: ''
},
variants: {
size: {
xs: {
icon: 'size-8 p-1.5',
iconIcon: 'size-4',
value: 'text-xl',
title: 'text-xs',
sparkline: 'h-8'
},
sm: {
icon: 'size-9 p-2',
iconIcon: 'size-4',
value: 'text-2xl',
title: 'text-sm',
sparkline: 'h-10'
},
md: {
icon: 'size-10 p-2',
iconIcon: 'size-5',
value: 'text-3xl',
title: 'text-sm',
sparkline: 'h-12'
},
lg: {
icon: 'size-12 p-2.5',
iconIcon: 'size-6',
value: 'text-4xl',
title: 'text-base',
sparkline: 'h-14'
},
xl: {
icon: 'size-14 p-3',
iconIcon: 'size-7',
value: 'text-5xl',
title: 'text-base',
sparkline: 'h-16'
}
},
color: {
primary: {
icon: 'bg-primary-500/10 text-primary-500 dark:text-primary-400',
iconIcon: 'text-primary-500 dark:text-primary-400',
trendIcon: 'text-primary-500 dark:text-primary-400',
sparklinePath: 'stroke-primary-500 dark:stroke-primary-400',
sparklineArea: 'fill-primary-500/20 dark:fill-primary-400/20'
},
secondary: {
icon: 'bg-secondary-500/10 text-secondary-500 dark:text-secondary-400',
iconIcon: 'text-secondary-500 dark:text-secondary-400',
trendIcon: 'text-secondary-500 dark:text-secondary-400',
sparklinePath: 'stroke-secondary-500 dark:stroke-secondary-400',
sparklineArea: 'fill-secondary-500/20 dark:fill-secondary-400/20'
},
success: {
icon: 'bg-success-500/10 text-success-500 dark:text-success-400',
iconIcon: 'text-success-500 dark:text-success-400',
trendIcon: 'text-success-500 dark:text-success-400',
sparklinePath: 'stroke-success-500 dark:stroke-success-400',
sparklineArea: 'fill-success-500/20 dark:fill-success-400/20'
},
info: {
icon: 'bg-info-500/10 text-info-500 dark:text-info-400',
iconIcon: 'text-info-500 dark:text-info-400',
trendIcon: 'text-info-500 dark:text-info-400',
sparklinePath: 'stroke-info-500 dark:stroke-info-400',
sparklineArea: 'fill-info-500/20 dark:fill-info-400/20'
},
warning: {
icon: 'bg-warning-500/10 text-warning-500 dark:text-warning-400',
iconIcon: 'text-warning-500 dark:text-warning-400',
trendIcon: 'text-warning-500 dark:text-warning-400',
sparklinePath: 'stroke-warning-500 dark:stroke-warning-400',
sparklineArea: 'fill-warning-500/20 dark:fill-warning-400/20'
},
error: {
icon: 'bg-error-500/10 text-error-500 dark:text-error-400',
iconIcon: 'text-error-500 dark:text-error-400',
trendIcon: 'text-error-500 dark:text-error-400',
sparklinePath: 'stroke-error-500 dark:stroke-error-400',
sparklineArea: 'fill-error-500/20 dark:fill-error-400/20'
},
neutral: {
icon: 'bg-gray-500/10 text-gray-500 dark:text-gray-400',
iconIcon: 'text-gray-500 dark:text-gray-400',
trendIcon: 'text-gray-500 dark:text-gray-400',
sparklinePath: 'stroke-gray-500 dark:stroke-gray-400',
sparklineArea: 'fill-gray-500/20 dark:fill-gray-400/20'
}
},
variant: {
solid: {
root: 'bg-inverted text-inverted',
title: 'text-dimmed',
value: 'text-inverted'
},
outline: {
root: 'bg-default ring ring-default',
title: 'text-muted',
value: 'text-default'
},
soft: {
root: 'bg-elevated/50',
title: 'text-toned',
value: 'text-default'
},
subtle: {
root: 'bg-elevated/50 ring ring-default',
title: 'text-toned',
value: 'text-default'
},
ghost: {
root: '',
title: 'text-muted',
value: 'text-default'
},
naked: {
root: '',
header: 'p-0 sm:p-0',
title: 'text-muted',
value: 'text-default'
}
}
},
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'outline'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
statCard: {
slots: {
root: 'rounded-lg overflow-hidden flex flex-col justify-between',
header: 'flex items-start gap-4 p-4 sm:p-6',
icon: 'flex items-center justify-center shrink-0 rounded-lg',
iconIcon: '',
content: 'flex-1 min-w-0 ',
title: 'text-sm font-medium text-muted',
value: 'text-2xl font-semibold text-default mt-1',
label: 'text-xs text-muted mt-1',
progress: '',
sparkline: '',
sparklineSvg: 'w-full',
sparklinePath: 'fill-none transition-all duration-300',
sparklineArea: 'transition-opacity duration-300',
sparklineZeroLine: 'stroke-default dark:stroke-default',
trend: 'flex items-center gap-1 text-xs font-medium mt-1',
trendIcon: 'shrink-0',
trendValue: ''
},
variants: {
size: {
xs: {
icon: 'size-8 p-1.5',
iconIcon: 'size-4',
value: 'text-xl',
title: 'text-xs',
sparkline: 'h-8'
},
sm: {
icon: 'size-9 p-2',
iconIcon: 'size-4',
value: 'text-2xl',
title: 'text-sm',
sparkline: 'h-10'
},
md: {
icon: 'size-10 p-2',
iconIcon: 'size-5',
value: 'text-3xl',
title: 'text-sm',
sparkline: 'h-12'
},
lg: {
icon: 'size-12 p-2.5',
iconIcon: 'size-6',
value: 'text-4xl',
title: 'text-base',
sparkline: 'h-14'
},
xl: {
icon: 'size-14 p-3',
iconIcon: 'size-7',
value: 'text-5xl',
title: 'text-base',
sparkline: 'h-16'
}
},
color: {
primary: {
icon: 'bg-primary-500/10 text-primary-500 dark:text-primary-400',
iconIcon: 'text-primary-500 dark:text-primary-400',
trendIcon: 'text-primary-500 dark:text-primary-400',
sparklinePath: 'stroke-primary-500 dark:stroke-primary-400',
sparklineArea: 'fill-primary-500/20 dark:fill-primary-400/20'
},
secondary: {
icon: 'bg-secondary-500/10 text-secondary-500 dark:text-secondary-400',
iconIcon: 'text-secondary-500 dark:text-secondary-400',
trendIcon: 'text-secondary-500 dark:text-secondary-400',
sparklinePath: 'stroke-secondary-500 dark:stroke-secondary-400',
sparklineArea: 'fill-secondary-500/20 dark:fill-secondary-400/20'
},
success: {
icon: 'bg-success-500/10 text-success-500 dark:text-success-400',
iconIcon: 'text-success-500 dark:text-success-400',
trendIcon: 'text-success-500 dark:text-success-400',
sparklinePath: 'stroke-success-500 dark:stroke-success-400',
sparklineArea: 'fill-success-500/20 dark:fill-success-400/20'
},
info: {
icon: 'bg-info-500/10 text-info-500 dark:text-info-400',
iconIcon: 'text-info-500 dark:text-info-400',
trendIcon: 'text-info-500 dark:text-info-400',
sparklinePath: 'stroke-info-500 dark:stroke-info-400',
sparklineArea: 'fill-info-500/20 dark:fill-info-400/20'
},
warning: {
icon: 'bg-warning-500/10 text-warning-500 dark:text-warning-400',
iconIcon: 'text-warning-500 dark:text-warning-400',
trendIcon: 'text-warning-500 dark:text-warning-400',
sparklinePath: 'stroke-warning-500 dark:stroke-warning-400',
sparklineArea: 'fill-warning-500/20 dark:fill-warning-400/20'
},
error: {
icon: 'bg-error-500/10 text-error-500 dark:text-error-400',
iconIcon: 'text-error-500 dark:text-error-400',
trendIcon: 'text-error-500 dark:text-error-400',
sparklinePath: 'stroke-error-500 dark:stroke-error-400',
sparklineArea: 'fill-error-500/20 dark:fill-error-400/20'
},
neutral: {
icon: 'bg-gray-500/10 text-gray-500 dark:text-gray-400',
iconIcon: 'text-gray-500 dark:text-gray-400',
trendIcon: 'text-gray-500 dark:text-gray-400',
sparklinePath: 'stroke-gray-500 dark:stroke-gray-400',
sparklineArea: 'fill-gray-500/20 dark:fill-gray-400/20'
}
},
variant: {
solid: {
root: 'bg-inverted text-inverted',
title: 'text-dimmed',
value: 'text-inverted'
},
outline: {
root: 'bg-default ring ring-default',
title: 'text-muted',
value: 'text-default'
},
soft: {
root: 'bg-elevated/50',
title: 'text-toned',
value: 'text-default'
},
subtle: {
root: 'bg-elevated/50 ring ring-default',
title: 'text-toned',
value: 'text-default'
},
ghost: {
root: '',
title: 'text-muted',
value: 'text-default'
},
naked: {
root: '',
header: 'p-0 sm:p-0',
title: 'text-muted',
value: 'text-default'
}
}
},
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'outline'
}
}
}
})
]
})