Migration to v4
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.
Migrate your project
From Nuxt UI Pro
- Replace
@nuxt/ui-pro
with@nuxt/ui
in yourpackage.json
:
pnpm remove @nuxt/ui-pro
pnpm add @nuxt/ui@alpha
yarn remove @nuxt/ui-pro
yarn add @nuxt/ui@alpha
npm uninstall @nuxt/ui-pro
npm install @nuxt/ui@alpha
bun remove @nuxt/ui-pro
bun add @nuxt/ui@alpha
- Replace
@nuxt/ui-pro
with@nuxt/ui
in yournuxt.config.ts
:
export default defineNuxtConfig({
modules: [
- '@nuxt/ui-pro',
+ '@nuxt/ui'
]
})
- Replace
@nuxt/ui-pro
with@nuxt/ui
in yourvite.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'
}
}
})
]
})
- Use the
ui
key instead ofuiPro
in yourapp.config.ts
:
export default defineAppConfig({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
},
+ pageCard: {
+ slots: {
+ root: 'rounded-xl',
+ }
+ }
},
- uiPro: {
- pageCard: {
- slots: {
- root: 'rounded-xl',
- }
- }
- }
})
- Use the
ui
key instead ofuiPro
in yourvite.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',
- }
- }
- }
})
]
})
- Replace
@nuxt/ui-pro
with@nuxt/ui
in your CSS:
@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
- Replace
@nuxt/ui-pro
with@nuxt/ui
in your imports:
- import type { BannerProps } from '@nuxt/ui-pro'
+ import type { BannerProps } from '@nuxt/ui'
From Nuxt UI
- When upgrading from Nuxt UI v3, you simply need to update to v4:
pnpm add @nuxt/ui@alpha
yarn add @nuxt/ui@alpha
npm install @nuxt/ui@alpha
bun add @nuxt/ui@alpha
Changes from v3
After upgrading to Nuxt UI v4, please note the following important changes:
Renamed ButtonGroup
The ButtonGroup
component has been renamed to FieldGroup
:
<template>
- <UButtonGroup>
+ <UFieldGroup>
<UButton label="Button" />
<UButton icon="i-lucide-chevron-down" />
+ </UFieldGroup>
- </UButtonGroup>
</template>
Renamed PageMarquee
The PageMarquee
component has been renamed to Marquee
:
<template>
- <UPageMarquee :items="items" />
+ <UMarquee :items="items" />
</template>
Removed PageAccordion
The PageAccordion
component has been removed in favor of Accordion
:
<template>
- <UPageAccordion :items="faqItems" />
+ <UAccordion :items="items" :unmount-on-hide="false" :ui="{ trigger: 'text-base', body: 'text-base text-muted' }" />
</template>
Renamed model modifiers
The modelModifiers
shape used by Input
, InputNumber
and Textarea
has changed in v4:
- The
nullify
modifier was renamed tonullable
(it converts empty/blank values tonull
). - A new
optional
modifier was added (it converts empty/blank values toundefined
).
- <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.
Changes to Form component
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:
- Schema transformations will only be applied to the
@submit
data and will no longer mutate the form's state. This provides better predictability and prevents unexpected state mutations. - Nested forms must be enabled explicitly using the
nested
prop. This makes the component behavior more explicit and prevents accidental nested form creation. - Nested forms should now provide a
name
prop (similar toUFormField
) 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>
AI SDK v5 migration (optional)
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.
- Update
@ai-sdk/vue
andai
dependencies in yourpackage.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 newChat
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(e: Event) {
+ e.preventDefault()
+ chat.sendMessage({ text: input.value })
+ input.value = ''
+ }
</script>
- Messages now use
parts
instead ofcontent
:
// 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>
- Some methods have been renamed:
// Regenerate the last message
- reload()
+ chat.regenerate()
// Access chat state
- :messages="messages"
- :status="status"
+ :messages="chat.messages"
+ :status="chat.status"
- New
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" unwrap="p" />
</template>
</UChatMessages>
</template>