Customize components
Tailwind Variants
Nuxt UI components are styled using the Tailwind Variants API, which provides a powerful way to create variants and manage component styles.
Slots
Components can have multiple slots
, each representing a distinct HTML element or section within the component. These slots allow for flexible content insertion and styling.
Let's take the Card component as an example which has multiple slots:
export default {
slots: {
root: 'bg-default ring ring-default divide-y divide-default rounded-lg',
header: 'p-4 sm:px-6',
body: 'p-4 sm:p-6',
footer: 'p-4 sm:px-6'
}
}
<template>
<div :class="ui.root({ class: [props.ui?.root, props.class] })">
<div :class="ui.header({ class: props.ui?.header })">
<slot name="header" />
</div>
<div :class="ui.body({ class: props.ui?.body })">
<slot />
</div>
<div :class="ui.footer({ class: props.ui?.footer })">
<slot name="footer" />
</div>
</div>
</template>
Some components don't have slots, they are just composed of a single root element. In this case, the theme only defines the base
slot like the Container component for example:
export default {
base: 'max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8'
}
<template>
<div :class="container({ class: props.class })">
<slot />
</div>
</template>
ui
prop, only the class
prop is available to override styles.Variants
Components support variants
, which allow you to dynamically adjust the styles of different slots
based on component props.
For example, the Avatar component uses a size
variant to control its appearance:
export default {
slots: {
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-elevated',
image: 'h-full w-full rounded-[inherit] object-cover'
},
variants: {
size: {
sm: {
root: 'size-7 text-sm'
},
md: {
root: 'size-8 text-base'
},
lg: {
root: 'size-9 text-lg'
}
}
},
defaultVariants: {
size: 'md'
}
}
This way, the size
prop will apply the corresponding styles to the root
slot:

<template>
<UAvatar src="https://github.com/nuxt.png" size="lg" />
</template>
Default Variants
The defaultVariants
property sets the default value for each variant when no prop is passed.
For example, the Avatar component has its default size set to md
:
export default {
slots: {
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-elevated',
image: 'h-full w-full rounded-[inherit] object-cover'
},
variants: {
size: {
sm: {
root: 'size-7 text-sm'
},
md: {
root: 'size-8 text-base'
},
lg: {
root: 'size-9 text-lg'
}
}
},
defaultVariants: {
size: 'md'
}
}
theme.defaultVariants
option in your nuxt.config.ts
to override the default values for size
and color
for all components at once.theme.defaultVariants
option in your vite.config.ts
to override the default values for size
and color
for all components at once.Compound Variants
Some components use the compoundVariants
property to apply classes when multiple variant conditions are met at the same time.
For example, the Button component uses the compoundVariants
property to apply classes for a specific color
and variant
combination:
import type { ModuleOptions } from '../module'
export default (options: Required<ModuleOptions>) => ({
slots: {
base: ['rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75', options.theme.transitions && 'transition-colors']
},
variants: {
color: {
...Object.fromEntries((options.theme.colors || []).map((color: string) => [color, ''])),
neutral: ''
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: '',
ghost: '',
link: ''
}
},
compoundVariants: [
...(options.theme.colors || []).map((color: string) => ({
color,
variant: 'outline',
class: `ring ring-inset ring-${color}/50 text-${color} hover:bg-${color}/10 active:bg-${color}/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-${color}`
})),
{
color: 'neutral',
variant: 'outline',
class: 'ring ring-inset ring-accented text-default bg-default hover:bg-elevated active:bg-elevated disabled:bg-default aria-disabled:bg-default focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
}
],
defaultVariants: {
color: 'primary',
variant: 'solid'
}
})
Customize theme
You have multiple ways to customize the appearance of Nuxt UI components, you can do it for all components at once or on a per-component basis.
tailwind-merge
under the hood to merge classes so you don't have to worry about conflicting classes.- Check the
Theme
section in the documentation of each individual component. - Browse the source code directly in the GitHub repository at
src/theme
.
Global config
You can override the theme of components globally inside your app.config.ts
by using the exact same structure as the theme object.
You can override the theme of components globally inside your vite.config.ts
by using the exact same structure as the theme object.
You can customize the slots
, variants
, compoundVariants
and defaultVariants
of a component to change the default theme of a component:
export default defineAppConfig({
ui: {
button: {
slots: {
base: 'font-bold'
},
variants: {
size: {
md: {
leadingIcon: 'size-4'
}
}
},
compoundVariants: [{
color: 'neutral',
variant: 'outline',
class: 'ring-default hover:bg-accented'
}],
defaultVariants: {
color: 'neutral',
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: {
button: {
slots: {
base: 'font-bold'
},
variants: {
size: {
md: {
leadingIcon: 'size-4'
}
}
},
compoundVariants: [{
color: 'neutral',
variant: 'outline',
class: 'ring-default hover:bg-accented'
}],
defaultVariants: {
color: 'neutral',
variant: 'outline'
}
}
}
})
]
})
font-bold
overrides font-medium
on all buttons, size-4
overrides size-5
class on the leading icon when size="md"
and ring-default hover:bg-accented
overrides ring-accented hover:bg-elevated
when color="neutral"
and variant="outline"
. The buttons now defaults to color="neutral"
and variant="outline"
.ui
prop
You can also override a component's slots using the ui
prop. This takes priority over both global config and resolved variants
.
<template>
<UButton
trailing-icon="i-lucide-chevron-right"
size="md"
color="neutral"
variant="outline"
:ui="{
trailingIcon: 'rotate-90 size-3'
}"
>
Button
</UButton>
</template>
trailingIcon
slot is overwritten with size-3
even though the md
size variant would apply a size-5
class to it.class
prop
The class
prop allows you to override the classes of the root
or base
slot. This takes priority over both global config and resolved variants
.
<template>
<UButton class="font-bold rounded-full">Button</UButton>
</template>
font-bold
class will override the default font-medium
class on this button.