Stat Card

GitHub
A component to display key statistics and metrics in a dashboard.

Usage

Use the StatCard component to display key metrics with an icon, title, value, and optional trend indicator.

Total Users
12,345
+12.5%
<template>
  <UStatCard
    icon="i-lucide-users"
    title="Total Users"
    value="12,345"
    :trend="12.5"
    trend-direction="up"
  />
</template>

With Trend Down

Use the trend-direction prop set to down to display a negative trend.

Sales
$45,231
+8.2%
<template>
  <UStatCard
    icon="i-lucide-shopping-cart"
    title="Sales"
    value="$45,231"
    :trend="8.2"
    trend-direction="down"
  />
</template>

Without Trend

You can omit the trend prop to hide the trend indicator.

Revenue
$125,430
<template>
  <UStatCard icon="i-lucide-dollar-sign" title="Revenue" value="$125,430" />
</template>

Icon

Use the icon prop to set the icon displayed in the card.

Active Sessions
1,234
+15.3%
<template>
  <UStatCard icon="i-lucide-activity" title="Active Sessions" value="1,234" :trend="15.3" />
</template>

Color

Use the color prop to change the color of the icon and trend indicator.

Completed
98.5%
+2.1%
<template>
  <UStatCard
    color="success"
    icon="i-lucide-check-circle"
    title="Completed"
    value="98.5%"
    :trend="2.1"
  />
</template>
Pending
23
+5.4%
<template>
  <UStatCard
    color="warning"
    icon="i-lucide-alert-triangle"
    title="Pending"
    value="23"
    :trend="5.4"
  />
</template>
Failed
12
+3.2%
<template>
  <UStatCard
    color="error"
    icon="i-lucide-x-circle"
    title="Failed"
    value="12"
    :trend="3.2"
    trend-direction="down"
  />
</template>

Progress Bar

Use the current and max props to display a progress bar. The show-label prop controls whether to show the "current / max" label.

Monthly Budget
8000
8000 / 10000
<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:

Storage Used
65
<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:

Sales Target
7500
7500 / 10000
+12.5%
<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" trend-direction="up" class="max-w-xl" />
</template>

Sparkline Chart

Use the data prop to display a sparkline chart. The show-area prop controls whether to show the area under the line.

Sales Trend
$45,231
<script setup lang="ts">
const data = ref([
  20,
  35,
  30,
  45,
  50,
  40,
  55,
  60,
  55,
  65,
  70,
  75
])
</script>

<template>
  <UStatCard icon="i-lucide-trending-up" title="Sales Trend" value="$45,231" :data="data" class="max-w-xl" />
</template>

With area:

Growth
24.5%
<script setup lang="ts">
const data = ref([
  15,
  20,
  25,
  30,
  35,
  40,
  45,
  50,
  55,
  60,
  65,
  70
])
</script>

<template>
  <UStatCard icon="i-lucide-bar-chart" title="Growth" value="24.5%" :data="data" show-area class="max-w-xl" />
</template>

With trend:

Performance
98.5%
+12.5%
<script setup lang="ts">
const data = ref([
  85,
  90,
  88,
  92,
  95,
  97,
  98
])
</script>

<template>
  <UStatCard icon="i-lucide-line-chart" title="Performance" value="98.5%" :data="data" :trend="12.5" trend-direction="up" show-area class="max-w-xl" />
</template>

Size

Use the size prop to change the size of the stat card.

Users
1,234
+5.2%
<template>
  <UStatCard size="xs" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
Users
1,234
+5.2%
<template>
  <UStatCard size="sm" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
Users
1,234
+5.2%
<template>
  <UStatCard size="md" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
Users
1,234
+5.2%
<template>
  <UStatCard size="lg" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
Users
1,234
+5.2%
<template>
  <UStatCard size="xl" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>

Variant

Use the variant prop to change the visual style of the card.

Users
1,234
+5.2%
<template>
  <UStatCard variant="solid" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>
Users
1,234
+5.2%
<template>
  <UStatCard variant="soft" icon="i-lucide-users" title="Users" value="1,234" :trend="5.2" />
</template>

Examples

Custom Slots

You can customize any part of the stat card using slots.

Growth
24.5%
vs last month
<template>
  <UStatCard icon="i-lucide-trending-up" title="Growth" value="24.5%">
    <template #trend> vs last month </template>
  </UStatCard>
</template>

Dashboard Grid

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>

API

Props

Prop Default Type
as'div'any

The element or component this component should render as.

iconany

The icon to display.

title string

The title/label of the stat.

value string | number

The main value to display.

trend number

The trend percentage value (e.g., 12.5 for +12.5%).

trendDirection'up' "up" | "down"

The direction of the trend.

current number

The current progress value (for progress bar).

max100 number

The maximum progress value (for progress bar).

showLabeltrueboolean

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.

strokeWidth2 number

Stroke width of the sparkline.

showAreafalseboolean

Show area under the sparkline.

height number

Height 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; trend?: ClassNameValue; trendIcon?: ClassNameValue; trendValue?: ClassNameValue; }

Slots

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; ui: object; }
trend{ trend?: number | undefined; trendDirection?: "up" | "down" | undefined; ui: object; }
default{ ui: object; }

Theme

app.config.ts
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',
        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'
      }
    }
  }
})
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({
      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',
            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'
          }
        }
      }
    })
  ]
})

Changelog

No recent changes