Nuxt UI provides multiple layers of customization, allowing you to tailor the design system to match your brand while maintaining consistency and type safety.
Customization Hierarchy
Theme customization follows a clear hierarchy:
Module Options (nuxt.config.ts) - Global theme configuration
App Config (app.config.ts) - Runtime theme overrides and color mappings
CSS Variables (assets/css/main.css) - Design token customization
Component Props - Per-instance customization
Each level overrides the previous, giving you fine-grained control.
Module Configuration
Configure the theme system in nuxt.config.ts:
export default defineNuxtConfig ({
modules: [ '@nuxt/ui' ] ,
ui: {
// Component prefix (default: 'U')
prefix: 'U' ,
theme: {
// Available color aliases
colors: [ 'primary' , 'secondary' , 'success' , 'info' , 'warning' , 'error' ],
// Enable/disable transitions
transitions: true ,
// Default variants for all components
defaultVariants: {
color: 'primary' ,
size: 'md'
},
// Tailwind CSS prefix
prefix: undefined // e.g., 'tw' for 'tw:bg-primary'
}
}
})
Adding Custom Colors
Extend the default color palette with custom aliases:
export default defineNuxtConfig ({
ui: {
theme: {
colors: [
'primary' ,
'secondary' ,
'success' ,
'info' ,
'warning' ,
'error' ,
'brand' , // Custom color
'accent' // Custom color
]
}
}
})
Then map them to Tailwind colors in app.config.ts:
export default defineAppConfig ({
ui: {
colors: {
primary: 'green' ,
secondary: 'blue' ,
success: 'green' ,
info: 'blue' ,
warning: 'yellow' ,
error: 'red' ,
brand: 'purple' , // Map to Tailwind color
accent: 'orange' , // Map to Tailwind color
neutral: 'slate'
}
}
})
The primary color is always required and will be automatically included even if not specified.
App Config Customization
The app.config.ts file is where you customize component themes at runtime:
Color Mappings
export default defineAppConfig ({
ui: {
colors: {
primary: 'indigo' ,
secondary: 'pink' ,
neutral: 'zinc'
}
}
})
Component Theme Overrides
Customize individual component themes:
export default defineAppConfig ({
ui: {
// Button customization
button: {
slots: {
base: 'font-semibold shadow-sm' ,
label: 'tracking-wide'
},
variants: {
size: {
'2xl' : {
base: 'px-6 py-4 text-xl gap-3' ,
leadingIcon: 'size-7' ,
trailingIcon: 'size-7'
}
},
variant: {
gradient: '' // Add new variant
}
},
compoundVariants: [
{
variant: 'gradient' ,
color: 'primary' ,
class: 'bg-gradient-to-r from-primary to-secondary text-white'
}
],
defaultVariants: {
variant: 'soft' ,
size: 'lg'
}
},
// Input customization
input: {
slots: {
base: 'border-2' // Thicker borders
},
defaultVariants: {
variant: 'outline' ,
size: 'lg'
}
},
// Badge customization
badge: {
slots: {
base: 'uppercase font-bold'
}
}
}
})
Icon Mapping
Customize the default icons used across components:
export default defineAppConfig ({
ui: {
icons: {
// Override default icons
chevronDown: 'i-heroicons-chevron-down-20-solid' ,
check: 'i-heroicons-check-20-solid' ,
close: 'i-heroicons-x-mark-20-solid' ,
search: 'i-heroicons-magnifying-glass-20-solid' ,
// Add custom icons
custom: 'i-custom-icon'
}
}
})
CSS Variable Customization
For deep customization, override CSS variables:
@import '#ui' ;
@layer theme {
:root {
/* Layout */
--ui-radius : 0.5 rem ; /* Larger border radius */
--ui-container : 90 rem ; /* Wider container */
--ui-header-height : 5 rem ; /* Taller header */
}
/* Light mode customization */
:root , .light {
--ui-bg : #fafafa ; /* Off-white background */
--ui-text : var ( --ui-color-neutral-800 ); /* Darker text */
--ui-border : var ( --ui-color-neutral-300 ); /* More visible borders */
}
/* Dark mode customization */
.dark {
--ui-bg : #0a0a0a ; /* Deeper black */
--ui-bg-elevated : var ( --ui-color-neutral-900 );
--ui-text-highlighted : var ( --ui-color-neutral-50 );
}
}
Custom Color Shades
Define completely custom color scales:
@layer theme {
:root {
/* Custom brand color scale */
--ui-color-brand-50 : #f0f9ff ;
--ui-color-brand-100 : #e0f2fe ;
--ui-color-brand-200 : #bae6fd ;
--ui-color-brand-300 : #7dd3fc ;
--ui-color-brand-400 : #38bdf8 ;
--ui-color-brand-500 : #0ea5e9 ;
--ui-color-brand-600 : #0284c7 ;
--ui-color-brand-700 : #0369a1 ;
--ui-color-brand-800 : #075985 ;
--ui-color-brand-900 : #0c4a6e ;
--ui-color-brand-950 : #082f49 ;
}
.light {
--ui-brand : var ( --ui-color-brand-500 );
}
.dark {
--ui-brand : var ( --ui-color-brand-400 );
}
}
Then use this color in your config:
export default defineAppConfig ({
ui: {
colors: {
brand: 'brand' // Reference your custom color
}
}
})
Advanced Patterns
Creating Component Presets
Create reusable component configurations:
export const buttonPresets = {
primary: {
variant: 'solid' ,
color: 'primary' ,
size: 'lg'
},
secondary: {
variant: 'outline' ,
color: 'secondary' ,
size: 'md'
},
danger: {
variant: 'solid' ,
color: 'error' ,
size: 'md'
}
} as const
Use in components:
< script setup lang = "ts" >
import { buttonPresets } from '~/config/ui-presets'
</ script >
< template >
< UButton v-bind = " buttonPresets . primary " > Submit </ UButton >
< UButton v-bind = " buttonPresets . secondary " > Cancel </ UButton >
< UButton v-bind = " buttonPresets . danger " > Delete </ UButton >
</ template >
Dynamic Theming
Change themes at runtime using updateAppConfig:
< script setup lang = "ts" >
import { updateAppConfig } from '#app'
function switchToPurpleTheme () {
updateAppConfig ({
ui: {
colors: {
primary: 'purple' ,
secondary: 'pink'
}
}
})
}
function switchToGreenTheme () {
updateAppConfig ({
ui: {
colors: {
primary: 'green' ,
secondary: 'blue'
}
}
})
}
</ script >
< template >
< div >
< UButton @ click = " switchToPurpleTheme " > Purple Theme </ UButton >
< UButton @ click = " switchToGreenTheme " > Green Theme </ UButton >
</ div >
</ template >
Responsive Variant Overrides
Create responsive component configurations:
< script setup lang = "ts" >
import { useBreakpoints } from '@vueuse/core'
const breakpoints = useBreakpoints ({
sm: 640 ,
md: 768 ,
lg: 1024
})
const buttonSize = computed (() => {
if ( breakpoints . isGreater ( 'lg' )) return 'xl'
if ( breakpoints . isGreater ( 'md' )) return 'lg'
return 'md'
})
</ script >
< template >
< UButton : size = " buttonSize " > Responsive Button </ UButton >
</ template >
Component-Specific Class Utilities
Create utility functions for consistent styling:
export function getCardClasses ( elevated = false ) {
return {
base: elevated ? 'shadow-lg' : 'shadow-sm' ,
body: 'space-y-4' ,
header: 'border-b border-default'
}
}
export function getInputClasses ( error ?: string ) {
return error ? {
base: 'ring-2 ring-error' ,
trailing: 'text-error'
} : {}
}
Use in components:
< script setup lang = "ts" >
import { getCardClasses , getInputClasses } from '~/utils/ui-classes'
const error = ref < string >()
</ script >
< template >
< UCard : class = " getCardClasses ( true ) " >
< UInput : class = " getInputClasses ( error ) " />
</ UCard >
</ template >
Brand Theming Example
Complete example of a custom brand theme:
export default defineNuxtConfig ({
ui: {
theme: {
colors: [ 'brand' , 'secondary' , 'success' , 'warning' , 'error' ],
defaultVariants: {
color: 'brand' ,
size: 'lg'
}
}
}
})
export default defineAppConfig ({
ui: {
colors: {
brand: 'violet' ,
secondary: 'fuchsia' ,
success: 'emerald' ,
warning: 'amber' ,
error: 'rose' ,
neutral: 'slate'
},
// Global component customization
button: {
slots: {
base: 'font-semibold shadow-sm hover:shadow-md transition-shadow'
},
defaultVariants: {
variant: 'soft'
}
},
input: {
defaultVariants: {
variant: 'outline' ,
size: 'lg'
}
},
card: {
slots: {
base: 'shadow-lg'
}
}
}
})
@import '#ui' ;
@layer theme {
:root {
--ui-radius : 0.75 rem ;
}
.light {
--ui-bg : #fafafa ;
}
.dark {
--ui-bg : #0f0f0f ;
}
}
Tailwind CSS Prefix
Avoid conflicts with other CSS frameworks by prefixing Tailwind utilities:
export default defineNuxtConfig ({
ui: {
theme: {
prefix: 'tw'
}
}
})
Now all Tailwind classes are prefixed:
< div class = "tw:bg-primary tw:text-white tw:p-4" >
Prefixed utilities
</ div >
When using a Tailwind prefix, you must also prefix custom classes in your theme overrides.
Disabling Transitions
For performance-critical applications, disable transitions globally:
export default defineNuxtConfig ({
ui: {
theme: {
transitions: false
}
}
})
This removes all transition-* classes from components.
Best Practices
Start with module config
Define your color palette and global settings in nuxt.config.ts first.
Map colors in app config
Use app.config.ts to map color aliases to Tailwind colors for runtime flexibility.
Use CSS variables for tokens
Customize design tokens (radius, spacing, colors) via CSS variables for deep customization.
Override components selectively
Only override the specific slots or variants you need, preserving the default behavior for others.
Test thoroughly
Always test customizations with all component variants and in both light/dark modes.
Document your theme
Create a style guide documenting your theme customizations for team consistency.
TypeScript Support
Nuxt UI provides full TypeScript support for theme customization:
import type { AppConfigUI } from '@nuxt/ui'
const uiConfig : AppConfigUI = {
colors: {
primary: 'blue' ,
neutral: 'slate'
},
button: {
defaultVariants: {
variant: 'soft' ,
size: 'lg'
}
}
}
export default defineAppConfig ({
ui: uiConfig
})
Design System Learn about the design system architecture
CSS Variables Complete CSS variable reference
Components Component-level theming with Tailwind Variants