Nuxt UI v4 marks a major milestone: Nuxt UI and Nuxt UI Pro are now unified into a single, fully open-source and free library. You now have access to 100+ production-ready components, all available in the @nuxt/ui package.
This guide provides step-by-step instructions to migrate your application from v3 to v4.
@nuxt/ui-pro with @nuxt/ui in your package.json:pnpm remove @nuxt/ui-pro
pnpm add @nuxt/ui
yarn remove @nuxt/ui-pro
yarn add @nuxt/ui
npm uninstall @nuxt/ui-pro
npm install @nuxt/ui
bun remove @nuxt/ui-pro
bun add @nuxt/ui
@nuxt/ui-pro with @nuxt/ui in your nuxt.config.ts:export default defineNuxtConfig({
modules: [
- '@nuxt/ui-pro',
+ '@nuxt/ui'
]
})
@nuxt/ui-pro with @nuxt/ui in your vite.config.ts:import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
- import uiPro from '@nuxt/ui-pro/vite'
+ import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
- uiPro({
+ ui({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
}
}
})
]
})
ui key instead of uiPro in your app.config.ts:export default defineAppConfig({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
},
+ pageCard: {
+ slots: {
+ root: 'rounded-xl',
+ }
+ }
},
- uiPro: {
- pageCard: {
- slots: {
- root: 'rounded-xl',
- }
- }
- }
})
ui key instead of uiPro in your vite.config.ts:export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
},
+ pageCard: {
+ slots: {
+ root: 'rounded-xl',
+ }
+ }
},
- uiPro: {
- pageCard: {
- slots: {
- root: 'rounded-xl',
- }
- }
- }
})
]
})
@nuxt/ui-pro with @nuxt/ui in your CSS:@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
@source directive to match the new directory structure.@import "tailwindcss";
@import "@nuxt/ui";
- @source "../../content/**/*";
+ @source "../../../content/**/*";
@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
@nuxt/ui-pro with @nuxt/ui in your imports:- import type { BannerProps } from '@nuxt/ui-pro'
+ import type { BannerProps } from '@nuxt/ui'
pnpm add @nuxt/ui
yarn add @nuxt/ui
npm install @nuxt/ui
bun add @nuxt/ui
After upgrading to Nuxt UI v4, please note the following important changes:
The ButtonGroup component has been renamed to FieldGroup:
<template>
- <UButtonGroup>
+ <UFieldGroup>
<UButton label="Button" />
<UButton icon="i-lucide-chevron-down" />
+ </UFieldGroup>
- </UButtonGroup>
</template>
The PageMarquee component has been renamed to Marquee:
<template>
- <UPageMarquee :items="items" />
+ <UMarquee :items="items" />
</template>
The PageAccordion component has been removed in favor of Accordion:
<template>
- <UPageAccordion
+ <UAccordion
:items="items"
+ :unmount-on-hide="false"
+ :ui="{ trigger: 'text-base', body: 'text-base text-muted' }"
/>
</template>
PageAccordion component was a wrapper that set unmount-on-hide to false and customized the ui prop.The modelModifiers shape used by Input, InputNumber and Textarea has changed in v4:
nullify modifier was renamed to nullable (it converts empty/blank values to null).optional modifier was added (it converts empty/blank values to undefined).- <UInput v-model.nullify="value" />
+ <UInput v-model.nullable="value" />
- <UTextarea v-model="value" :model-modifiers="{ nullify: true }" />
+ <UTextarea v-model="value" :model-modifiers="{ nullable: true }" />
Use nullable when you want empty values as null, and optional when you prefer undefined for absent values.
The Form component has been improved in v4 with better state management and nested form handling. Here are the key changes you need to be aware of:
@submit data and will no longer mutate the form's state. This provides better predictability and prevents unexpected state mutations.nested prop. This makes the component behavior more explicit and prevents accidental nested form creation.name prop (similar to UFormField) and will automatically inherit their state from their parent form.<template>
<UForm :state="state" :schema="schema" @submit="onSubmit">
<UFormField label="Customer" name="customer">
<UInput v-model="state.customer" placeholder="Wonka Industries" />
</UFormField>
<div v-for="(item, index) in state.items" :key="index">
<UForm
- :state="item"
+ :name="`items.${index}`"
:schema="itemSchema"
+ nested
>
<UFormField :label="!index ? 'Description' : undefined" name="description">
<UInput v-model="item.description" />
</UFormField>
<UFormField :label="!index ? 'Price' : undefined" name="price">
<UInput v-model="item.price" type="number" />
</UFormField>
</UForm>
</div>
</UForm>
</template>
Some Nuxt Content utilities that were previously available in Nuxt UI Pro have been removed in v4:
findPageBreadcrumbfindPageHeadlineThese are now fully provided by Nuxt Content. Make sure to update your imports and usage accordingly.
- import { findPageHeadline } from '@nuxt/ui-pro/utils/content'
+ import { findPageHeadline } from '@nuxt/content/utils'
- import { findPageBreadcrumb } from '@nuxt/ui-pro/utils/content'
+ import { findPageBreadcrumb } from '@nuxt/content/utils'
This section only applies if you're using the AI SDK and chat components (ChatMessage, ChatMessages, ChatPrompt, ChatPromptSubmit, ChatPalette). If you're not using AI features, you can skip this section.
@ai-sdk/vue and ai dependencies in your package.json:{
"dependencies": {
- "@ai-sdk/vue": "^1.2.x",
+ "@ai-sdk/vue": "^2.0.x",
- "ai": "^4.3.x"
+ "ai": "^5.0.x"
}
}
useChat composable has been replaced with the new Chat class:<script setup lang="ts">
- import { useChat } from '@ai-sdk/vue'
+ import { Chat } from '@ai-sdk/vue'
+ import type { UIMessage } from 'ai'
- const { messages, input, handleSubmit, status, error, reload, setMessages } = useChat()
+ const messages: UIMessage[] = []
+ const input = ref('')
+
+ const chat = new Chat({
+ messages
+ })
+
+ function handleSubmit() {
+ chat.sendMessage({ text: input.value })
+ input.value = ''
+ }
</script>
parts instead of content:// When manually creating messages
- setMessages([{
+ messages.push({
id: '1',
role: 'user',
- content: 'Hello world'
+ parts: [{ type: 'text', text: 'Hello world' }]
- }])
+ })
// In templates
<template>
- <UChatMessage :content="message.content" />
+ <UChatMessage :parts="message.parts" />
</template>
// Regenerate the last message
- reload()
+ chat.regenerate()
// Access chat state
- :messages="messages"
- :status="status"
+ :messages="chat.messages"
+ :status="chat.status"
getTextFromMessage utility to extract text from AI SDK v5 message parts:<script setup lang="ts">
import { getTextFromMessage } from '@nuxt/ui/utils/ai'
</script>
<template>
<UChatMessages :messages="chat.messages" :status="chat.status">
<template #content="{ message }">
<!-- Extract text from message parts and render with MDC -->
<MDC :value="getTextFromMessage(message)" :cache-key="message.id" class="*:first:mt-0 *:last:mb-0" />
</template>
</UChatMessages>
</template>