✨ Highlights
📆 Calendar month and year selection
The Calendar component gains a single type prop (date | month | year, default date) that renders it as a day, month or year picker, covering both standalone pickers and quick navigation:
<script setup lang="ts">
const value = shallowRef(new CalendarDate(2026, 6, 17))
</script>
<template>
<UCalendar v-model="value" type="month" />
</template>Tip
In date mode the heading also becomes a clickable button that cycles day → month → year, so you can jump to a month or year without clicking through prev / next repeatedly. This is controlled by the new viewControls and viewButton props and works with range.
🧭 useTour composable
The new useTour composable drives guided tours by re-anchoring a single Popover across steps. It owns the step state and resolves each step's target into a reference you bind to <UPopover>, while you keep full control over the content and navigation:
<script setup lang="ts">
const card = useTemplateRef('card')
const tour = useTour([
{ target: '#cta', title: 'Get started' },
{ target: () => card.value, title: 'Profile', side: 'right' },
{ target: null, title: 'All set' }
])
</script>
<template>
<UButton @click="tour.start()">Start tour</UButton>
<UPopover :open="tour.open.value" :reference="tour.reference.value" :dismissible="false">
<template #content>
<!-- your content + buttons -->
<UButton :disabled="!tour.hasPrev.value" @click="tour.prev()">Back</UButton>
<UButton @click="tour.next()">{{ tour.hasNext.value ? 'Next' : 'Finish' }}</UButton>
</template>
</UPopover>
</template>✂️ Override default classes
A new build-time theme.unstyled option strips Nuxt UI's default theme classes from every component, keeping only their structure and the classes you provide through class, ui or app.config.ui. This lets you bring your own design system on top of the components' logic and accessibility, or cut HTML and bundle bloat:
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
theme: {
unstyled: true
}
}
})Warning
This strips structural classes too (positioning, transitions, flex/grid), not just cosmetic ones. Layout-heavy components like Modal, Drawer or Calendar will need you to re-supply their layout.
For more surgical control, slot classes can now be a (defaults) => classes function that replaces the slot's defaults instead of merging onto them. It receives the resolved defaults so you can reuse part of them, while plain strings keep merging exactly as before. It works in :ui, app.config.ui and <UTheme :ui>:
<template>
<UButton :ui="{ base: () => 'text-3xl font-bold' }" label="Button" />
</template>🎯 Uniform focus styles
Every component now shares a single focus-visible language: a soft outline halo tinted with the component's color (e.g. outline-primary/25), so the indicator stays consistent and accessible across every variant and color. Tab panels, scrollable regions and overlay links that browsers make focusable now show the halo too, instead of hiding focus entirely.
If you prefer one outline color across your whole app regardless of component color, a single global rule in your main.css does it:
*,
::before,
::after {
@apply outline-primary/25;
}
*:focus-visible,
*:has(> a:focus-visible) {
--tw-ring-color: var(--ui-primary);
}Note
Check out the before/after preview images in #6576.
🚀 Features
- Calendar: add month and year selection (#6582) (ca3c72e)
- ChatMessages: expose
registerMessageRef(#6275) (f99778e) - components: allow hiding
iconwithfalse(#6597) (09eb639) - ContentSearch/DashboardSearch: forward input config to command palette (#6312) (5199b7e)
- FileUpload: expose
removeFilein slots (#6492) (4da3a5d) - Modal/Slideover: add
leaveandenterevents (#6596) (006324a) - module: add
theme.unstyledoption (#6551) (a2a8bc9) - PinInput: add
separatorprop (#6392) (89b30e8) - ScrollArea: add
shadowprop (#6561) (d850ae6) - Select/SelectMenu: use
multiplein theme (#6554) (f66eb65) - Sidebar: add
transitionprop (#6484) (af80a67) - theme: allow replacing slot classes with a function (#6562) (ea576e2)
- theme: uniformize focus styles across components (#6576) (b026ca2)
- useTour: new composable (#6557) (dc05151)
- vite: add
rootoption to override.nuxt-uidirectory location (#6595) (cccb3d5)
🐛 Bug Fixes
- CommandPalette: only scroll to highlighted item when focused (#6579) (02259a6)
- Link: set default for locale prop (#6563) (e9ab758)
- module: remove inline script in SPA mode for strict CSP (#6577) (7225e9f)
- ProseCodeCollapse: cap root
max-heightinstead of toggling preheight(#6565) (52d3c45) - ProseKbd: type default slot as
VNode[](52367b1) - SelectMenu: bind
idand aria attributes on trigger (#6572) (c3bef7a) - Select: open menu on label click (#6575) (e8d18c3)
- Tabs: render active indicator during SSR (#6570) (9e5b8a6)
- templates: resolve vite root to an absolute path for #build aliases (#6586) (238e291)
🌐 Locales
❤️ Contributors
- @benjamincanac
- @innocenzi
- @zAlweNy26
- @maximepvrt
- @OrbisK
- @mikenewbon
- @al1maher
- @emilsgulbis
- @martinszeltins
- @cydrickn
- @neukinal
- @hendrikheil
- @johnson-jnr
- @ReCoN-96
Full Changelog: v4.8.2...v4.9.0