---
description: 'Learn how to customize the look and feel of the components.'
---

This module relies on Nuxt [App Config](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) file to customize the look and feel of the components at runtime with HMR (hot-module-replacement).

## Colors

### Configuration

Components are based on a `primary` and a `gray` color. You can change them in your `app.config.ts`.

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    primary: 'green',
    gray: 'cool'
  }
})
```

::callout{icon="i-heroicons-light-bulb"}
Try to change the `primary` and `gray` colors by clicking on the :u-icon{name="i-heroicons-swatch-20-solid" class="w-4 h-4 align-middle text-primary-500 dark:text-primary-400"} button in the header.
::

As this module uses Tailwind CSS under the hood, you can use any of the [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference) or your own custom colors. By default, the `primary` color is `green` and the `gray` color is `cool`.

When [using custom colors](https://tailwindcss.com/docs/customizing-colors#using-custom-colors) or [adding additional colors](https://tailwindcss.com/docs/customizing-colors#adding-additional-colors) through the `extend` key in your `tailwind.config.ts`, you'll need to make sure to define all the shades from `50` to `950` as most of them are used in the components config defined in [`ui.config.ts`](https://github.com/nuxt/ui/blob/dev/src/runtime/ui.config.ts). You can [generate your colors](https://tailwindcss.com/docs/customizing-colors#generating-colors) using tools such as https://uicolors.app/ for example.

```ts [tailwind.config.ts]
import type { Config } from 'tailwindcss'
import defaultTheme from 'tailwindcss/defaultTheme'

export default <Partial<Config>>{
  theme: {
    extend: {
      colors: {
        green: {
          50: '#EFFDF5',
          100: '#D9FBE8',
          200: '#B3F5D1',
          300: '#75EDAE',
          400: '#00DC82',
          500: '#00C16A',
          600: '#00A155',
          700: '#007F45',
          800: '#016538',
          900: '#0A5331',
          950: '#052e16'
        }
      }
    }
  }
}
```

### CSS Variables

To provide dynamic colors that can be changed at runtime, this module uses CSS variables. As Tailwind CSS already has a `gray` color, the module automatically renames it to `cool` to avoid conflicts (`coolGray` was renamed to `gray` when Tailwind CSS v3.0 was released).

Likewise, you can't define a `primary` color in your `tailwind.config.ts` as it would conflict with the `primary` color defined by the module.

::callout{icon="i-heroicons-light-bulb"}
We'd advise you to use those colors in your components and pages, e.g. `text-primary-500 dark:text-primary-400`, `bg-gray-100 dark:bg-gray-900`, etc. so your app automatically adapts when changing your `app.config.ts`.
::

The `primary` color also has a `DEFAULT` shade that changes based on the theme. It is `500` in light mode and `400` in dark mode. You can use as a shortcut in your components and pages, e.g. `text-primary`, `bg-primary`, `focus-visible:ring-primary`, etc.

### Smart Safelisting

Components having a `color` prop like [Avatar](/components/avatar#chip), [Badge](/components/badge#style), [Button](/components/button#style), [Input](/components/input#style) (inherited in [Select](/components/select) and [SelectMenu](/components/select-menu)), [RadioGroup](/components/radio-group), [Checkbox](/components/checkbox), [Toggle](/components/toggle), [Range](/components/range) and [Notification](/components/notification#timeout) will use the `primary` color by default but will handle all the colors defined in your `tailwind.config.ts` or the default Tailwind CSS colors.

Variant classes of those components are defined with a syntax like `bg-{color}-500 dark:bg-{color}-400` so they can be used with any color. However, this means that Tailwind will not find those classes and therefore will not generate the corresponding CSS.

The module uses the [Tailwind CSS safelist](https://tailwindcss.com/docs/content-configuration#safelisting-classes) feature to force the generation of all the classes for the `primary` color **only** as it is the default color for all the components.

Then, the module will automatically detect when you use one of those components with a color and will safelist it for you. This means that if you use a `red` color for a Button component, the `red` color classes will be safelisted for the Button component only. This will allow to keep the CSS bundle size as small as possible.

There is one case where you would want to force the safelisting of a color. For example, if you've set the default color of the Button component to `orange` in your `app.config.ts`.

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    button: {
      default: {
        color: 'orange'
      }
    }
  }
})
```

This will apply the orange color when using a default `<UButton />`. You'll need to safelist this color manually in your `nuxt.config.ts` ui options as we won't be able to detect it automatically. You can do so through the `safelistColors` option which defaults to `['primary']`.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  ui: {
    safelistColors: ['orange']
  }
})
```

This can also happen when you bind a dynamic color to a component: `<UBadge :color="color" />`, `<UAvatar :chip-color="statuses[user.status]" />`, etc. In this case, you'll need to safelist the possible color values manually as well.

## Components

### `app.config.ts`

Components are styled with Tailwind CSS but classes are all defined in the default [ui.config.ts](https://github.com/nuxt/ui/blob/dev/src/runtime/ui.config.ts) file. You can override those in your own `app.config.ts`.

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    container: {
      constrained: 'max-w-5xl'
    }
  }
})
```

Thanks to [tailwind-merge](https://github.com/dcastil/tailwind-merge), the `app.config.ts` is smartly merged with the default config. This means you don't have to rewrite everything.

You can change this behavior by setting `strategy` to `override` in your `app.config.ts`:

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    strategy: 'override',
    button: {
      color: {
        white: {
          solid: 'bg-white dark:bg-gray-900'
        }
      }
    }
  }
})
```

### `ui` prop

Each component has a `ui` prop that allows you to customize everything specifically.

```vue
<template>
  <UContainer :ui="{ constrained: 'max-w-2xl' }">
    <slot />
  </UContainer>
</template>
```

::callout{icon="i-heroicons-light-bulb"}
You can find the default classes for each component under the `Config` section.
::

Thanks to [tailwind-merge](https://github.com/dcastil/tailwind-merge), the `ui` prop is smartly merged with the config. This means you don't have to rewrite everything.

For example, the default preset of the `FormGroup` component looks like this:

```json
{
  "label": {
    "base": "block font-medium text-gray-700 dark:text-gray-200"
  }
}
```

To change the font of the `label`, you only need to write:

```vue
<UFormGroup name="email" label="Email" :ui="{ label: { base: 'font-semibold' } }" />
```

This will smartly replace the `font-medium` by `font-semibold` and prevent any class duplication and any class priority issue.

You can change this behavior by setting `strategy` to `override` inside the `ui` prop:

```vue
<UButton
  to="https://github.com/nuxt/ui"
  :ui="{
    strategy: 'override',
    color: {
      white: {
        solid: 'bg-white dark:bg-gray-900'
      }
    }
  }"
/>
```

### `class` attribute

You can also use the `class` attribute to add classes to the component.

```vue
<template>
  <UButton label="Button" class="rounded-full" />
</template>
```

Again, with [tailwind-merge](https://github.com/dcastil/tailwind-merge), this will smartly merge the classes with the `ui` prop and the config.

### Default values

Some component props like `size`, `color`, `variant`, etc. have a default value that you can override in your `app.config.ts`.

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    button: {
      default: {
        size: 'md',
        color: 'gray',
        variant: 'ghost'
      }
    }
  }
})
```

## Dark mode

All the components are styled with dark mode in mind.

Thanks to [Tailwind CSS dark mode](https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually) class strategy and the [@nuxtjs/color-mode](https://github.com/nuxt-modules/color-mode) module, you literally have nothing to do.

You can disable dark mode by setting the `preference` to `light` instead of `system` in your `nuxt.config.ts`.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  colorMode: {
    preference: 'light'
  }
})
```

::callout{icon="i-heroicons-light-bulb"}
If you're stuck in dark mode even after changing this setting, you might need to remove the `nuxt-color-mode` entry from your browser's local storage.
::

You can easily build a color mode button by using the `useColorMode` composable from `@nuxtjs/color-mode`.

::component-example
#default
:color-mode-button

#code
```vue
<script setup>
const colorMode = useColorMode()
const isDark = computed({
  get () {
    return colorMode.value === 'dark'
  },
  set () {
    colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
  }
})
</script>
<template>
  <ClientOnly>
    <UButton
      :icon="isDark ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
      color="gray"
      variant="ghost"
      aria-label="Theme"
      @click="isDark = !isDark"
    />
    <template #fallback>
      <div class="w-8 h-8" />
    </template>
  </ClientOnly>
</template>
```
::

## Icons

You can use any icon (100,000+) from [Iconify](https://iconify.design/).

Some components have an `icon` prop that allows you to add an icon to the component.

```vue
<template>
  <UButton icon="i-heroicons-magnifying-glass" />
</template>
```

You can also use the [Icon](/components/icon) component to add an icon anywhere in your app by following this pattern: `i-{collection_name}-{icon_name}`.

```vue
<template>
  <UIcon name="i-heroicons-moon" />
</template>
```

### Collections

By default, the module uses [Heroicons](https://heroicons.com/) but you can change it from the module options in your `nuxt.config.ts`.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  ui: {
    icons: ['mdi', 'simple-icons']
  }
})
```

::callout{icon="i-heroicons-light-bulb"}
Search the icon you want to use on https://icones.js.org built by [@antfu](https://github.com/antfu).
::

Thanks to [@egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons) plugin, only the icons you use in your app will be bundled in your CSS. However, you need to install the icon collections you specified in the `ui.icons` key:

::code-group

```bash [pnpm]
pnpm i @iconify-json/{collection_name}
```

```bash [yarn]
yarn add @iconify-json/{collection_name}
```

```bash [npm]
npm install @iconify-json/{collection_name}
```

::

If you choose to use the full `@iconify/json` icon collection (50MB), you can specifiy `icons: 'all'` or `icons: {}` in your `nuxt.config.ts` to use any icon in your app.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  ui: {
    icons: {}
  }
})
```

### Custom config

If you have specific needs, like using a custom icon collection, you can use the `icons` option in your `nuxt.config.ts` as an object to override the config of the [@egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons#plugin-options) plugin.

```ts [nuxt.config.ts]
import { getIconCollections } from '@egoist/tailwindcss-icons'

export default defineNuxtConfig({
  ui: {
    icons: {
      // might solve stretch bug on generate, see https://github.com/egoist/tailwindcss-icons/issues/23
      extraProperties: {
        'mask-size': 'contain',
        'mask-position': 'center'
      },
      collections: {
        foo: {
          icons: {
            'arrow-left': {
              // svg body
              body: '<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" />',
              // svg width and height, optional
              width: 24,
              height: 24
            }
          }
        },
        ...getIconCollections(['heroicons', 'simple-icons'])
      }
    }
  }
})
```

### Dynamic icons

The `Icon` component also has a `dynamic` prop to use the [nuxt-icon](https://github.com/nuxt-modules/icon/) module instead of the [@egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons#plugin-options) plugin.

Read more about this in the [Icon](/components/icon#dynamic) component page.

### Defaults

You can easily replace all the default icons of the components in your `app.config.ts`.

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    button: {
      default: {
        loadingIcon: 'i-octicon-sync-24'
      }
    },
    input: {
      default: {
        loadingIcon: 'i-octicon-sync-24'
      }
    },
    select: {
      default: {
        loadingIcon: 'i-octicon-sync-24',
        trailingIcon: 'i-octicon-chevron-down-24'
      }
    },
    selectMenu: {
      default: {
        selectedIcon: 'i-octicon-check-24'
      }
    },
    notification: {
      default: {
        closeButton: {
          icon: 'i-octicon-x-24'
        }
      }
    },
    commandPalette: {
      default: {
        icon: 'i-octicon-search-24',
        loadingIcon: 'i-octicon-sync-24',
        selectedIcon: 'i-octicon-check-24',
        emptyState: {
          icon: 'i-octicon-search-24'
        }
      }
    },
    table: {
      default: {
        sortAscIcon: 'i-octicon-sort-asc-24',
        sortDescIcon: 'i-octicon-sort-desc-24',
        sortButton: {
          icon: 'i-octicon-arrow-switch-24'
        },
        loadingState: {
          icon: 'i-octicon-sync-24'
        },
        emptyState: {
          icon: 'i-octicon-database-24'
        }
      }
    },
    pagination: {
      default: {
        firstButton: {
          icon: 'i-octicon-chevron-left-24'
        },
        prevButton: {
          icon: 'i-octicon-arrow-left-24'
        },
        nextButton: {
          icon: 'i-octicon-arrow-right-24'
        },
        lastButton: {
          icon: 'i-octicon-chevron-right-24'
        }
      }
    },
    accordion: {
      default: {
        openIcon: 'i-octicon-chevron-down-24'
      }
    },
    breadcrumb: {
      default: {
        divider: 'i-octicon-chevron-right-24'
      }
    }
  }
})
```
