v 4への移行

アプリケーションをNuxt UI v3からNuxt UI v4に移行するための包括的なガイド。

Nuxt UI v4は大きなマイルストーンとなりました。** Nuxt UIとNuxt UI Proは、完全にオープンソースで無料のライブラリ**に統合されました。125以上のプロダクション対応コンポーネントにアクセスでき、すべて@nuxt/uiパッケージで利用できます。

Nuxt UI v4は、依存関係のため** Nuxt 4.1以降の**が必要です。Nuxt UI v4に移行する前に必ずアップグレードしてください。

このガイドでは、アプリケーションをv 3からv 4に移行するためのステップバイステップの手順を説明します。

プロジェクトの移行

Nuxt UI Proから

  1. @nuxt/ui-pro``@nuxt/ui``@nuxt/ui-proを@@に置き換えてください。
pnpm remove @nuxt/ui-pro
pnpm add @nuxt/ui tailwindcss
  1. @nuxt/ui-pro``@nuxt/ui``nuxt.config.tsに置き換えてください。
nuxt.config.ts
export default defineNuxtConfig({
  modules: [
-   '@nuxt/ui-pro',
+   '@nuxt/ui'
  ]
})
  1. vite.config.tsの@nuxt/ui-pro``@nuxt/uiに置き換えてください。
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'
        }
      }
    })
  ]
})
  1. app.config.tsでuiProの代わりにuiキーを使用します。
app/app.config.ts
export default defineAppConfig({
  ui: {
    colors: {
      primary: 'green',
      neutral: 'slate'
    },
+   pageCard: {
+     slots: {
+       root: 'rounded-xl',
+     }
+   }
  },
- uiPro: {
-   pageCard: {
-     slots: {
-       root: 'rounded-xl',
-     }
-   }
- }
})
  1. vite.config.tsでuiProの代わりにuiキーを使用します。
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',
-         }
-       }
-     }
    })
  ]
})
  1. CSSで@nuxt/ui-proを@nuxt/uiに置き換えてください。
app/assets/css/main.css
@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
Nuxt UI v4と同時にNuxt 4にアップグレードする場合は、@sourceディレクティブを新しいディレクトリ構造に合わせて更新してください。
app/assets/css/main.css
@import "tailwindcss";
@import "@nuxt/ui";

- @source "../../content/**/*";
+ @source "../../../content/**/*";
src/assets/css/main.css
@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
  1. インポートの@nuxt/ui-proを@nuxt/uiに置き換えてください。
- import type { BannerProps } from '@nuxt/ui-pro'
+ import type { BannerProps } from '@nuxt/ui'

Nuxt UIから

  1. Nuxt UI v3からアップグレードする場合は、単にv4にアップデートする必要があります。
pnpm add @nuxt/ui tailwindcss

v3からの変更点

Nuxt UI v4にアップグレードした後、以下の重要な変更点に注意してください。

Renamed ButtonGroup

ButtonGroupコンポーネントは、FieldGroupに改名されました。

<template>
- <UButtonGroup>
+ <UFieldGroup>
    <UButton label="Button" />
    <UButton icon="i-lucide-chevron-down" />
+ </UFieldGroup>
- </UButtonGroup>
</template>

Renamed PageMarquee

PageMarqueeコンポーネントは、Marqueeに改名されました。

<template>
- <UPageMarquee :items="items" />
+ <UMarquee :items="items" />
</template>

削除されたPageAccordion

PageAccordionコンポーネントは削除され、Accordionに置き換えられました。

<template>
- <UPageAccordion
+ <UAccordion
    :items="items"
+   :unmount-on-hide="false"
+   :ui="{ trigger: 'text-base', body: 'text-base text-muted' }"
  />
</template>
PageAccordionコンポーネントは、unmount-on-hideをfalseに設定し、ui propをカスタマイズするラッパーです。

名前変更モデル修飾子

Input[InputNumber](/docs/components/input-number PH27 Textareaで使用されるmodelModifiers``modelModifiers``modelModifiers PH2222212121

  1. nullify修飾子がnullableに改名されました(空/空白値をnullに変換します)。
  2. 新しいoptional修飾子が追加されました(空/空白の値を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 }" />

空の値をnullとして使用したい場合はnullableを使用し、存在しない値をundefinedとして使用したい場合はoptionalを使用します。

フォームコンポーネントの変更

Formコンポーネントはv4で改善され、状態管理とネストされたフォーム処理が改善されました。注意すべき主な変更点は次のとおりです。

  1. Schema 変換は@submit data **にのみ適用され、フォームの状態を変更しなくなります。これにより予測可能性が向上し、予期しない状態の変異を防ぎます。
  2. ネストされたフォームは、nested propを使用して明示的にを有効にする必要があります。これにより、コンポーネントの動作がより明確になり、偶発的なネストされたフォームの作成を防ぎます。
  3. ネストされたフォームは、name prop UFormFieldに似ていますを提供する必要があり、自動的に親フォームから状態を継承します。
<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>

削除された非推奨ユーティリティ

以前Nuxt UI Proで利用可能だった** Nuxtコンテンツユーティリティの一部は、v4で削除されました。

  • findPageBreadcrumb
  • findPageHeadline

これらはNuxt Contentによって完全に提供されます。インポートと使用方法を適宜更新してください。

- 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'

AI SDK移行(オプション)

このセクションは、AI SDKとチャットコンポーネント(ChatMessage、ChatMessages、ChatPrompt、ChatPromptSubmit、ChatPalette)を使用している場合にのみ適用されます。AI機能を使用していない場合は、このセクションをスキップしてください。

  1. package.jsonの@ai-sdk/vueとaiの依存関係を更新し、 Comark を追加しますストリーミングMarkdownとしてAIレスポンスをレンダリングするために@nuxtjs/mdcを置き換えます
{
  "dependencies": {
-   "@ai-sdk/vue": "^1.2.x",
+   "@ai-sdk/vue": "^4.0.x",
-   "ai": "^4.3.x",
+   "ai": "^7.0.x",
+   "@comark/nuxt": "latest"
  }
}

モジュールに@comark/nuxtを追加します。

nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/ui',
+   '@comark/nuxt'
  ]
})
{
  "dependencies": {
-   "@ai-sdk/vue": "^1.2.x",
+   "@ai-sdk/vue": "^4.0.x",
-   "ai": "^4.3.x",
+   "ai": "^7.0.x",
+   "@comark/vue": "latest"
  }
}
  1. useChat構成可能なAPIが変更されました。inputとhandleSubmitは削除され、sendMessageに置き換えられました。messagesは直接設定するrefになりました。
<script setup lang="ts">
  import { useChat } from '@ai-sdk/vue'

- const { messages, input, handleSubmit, status, error, reload, setMessages } = useChat()
+ const { messages, status, error, sendMessage, regenerate, stop } = useChat()
+ const input = ref('')
+
+ function handleSubmit() {
+   sendMessage({ text: input.value })
+   input.value = ''
+ }
</script>
  1. メッセージがcontentの代わりにpartsを使用するようになりました。
// When manually creating messages
- setMessages([{
+ messages.value = [{
  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>
  1. いくつかのメソッドが名前変更されました
// Regenerate the last message
- reload()
+ regenerate()
  1. <MDC>を<Markdown>に置き換えてください。MarkdownコンポーネントはMarkdownのストリーミング用に作られており、AIから到着したトークンを段階的にレンダリングします。AI SDKヘルパーと@nuxt/ui/utils/aiのisPartStreamingユーティリティを使用してください。
shikiプラグインを使用する場合は、ダークモードをサポートするためにスタイルシートに次のCSSを追加します。
main.css
html.dark .shiki span {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
  font-style: var(--shiki-dark-font-style) !important;
  font-weight: var(--shiki-dark-font-weight) !important;
  text-decoration: var(--shiki-dark-text-decoration) !important;
}
<script setup lang="ts">
import { isReasoningUIPart, isTextUIPart } from 'ai'
import { isPartStreaming } from '@nuxt/ui/utils/ai'
import shiki from '@comark/nuxt/plugins/shiki'

const plugins = [shiki()]
</script>

<template>
  <UChatMessages :messages="messages" :status="status">
    <template #content="{ message }">
      <template v-for="(part, index) in message.parts" :key="`${message.id}-${part.type}-${index}`">
        <UChatReasoning
          v-if="isReasoningUIPart(part)"
          :text="part.text"
          :streaming="isPartStreaming(part)"
        >
          <Markdown
            :value="part.text"
            :streaming="isPartStreaming(part)"
            :plugins="plugins"
            class="*:first:mt-0 *:last:mb-0"
          />
        </UChatReasoning>

        <template v-else-if="isTextUIPart(part)">
          <Markdown
            v-if="message.role === 'assistant'"
            :value="part.text"
            :streaming="isPartStreaming(part)"
            :plugins="plugins"
            class="*:first:mt-0 *:last:mb-0"
          />
          <p v-else-if="message.role === 'user'" class="whitespace-pre-wrap">
            {{ part.text }}
          </p>
        </template>
      </template>
    </template>
  </UChatMessages>
</template>
<script setup lang="ts">
import { isReasoningUIPart, isTextUIPart } from 'ai'
import { isPartStreaming } from '@nuxt/ui/utils/ai'
import { Markdown } from '@comark/vue'
import shiki from '@comark/vue/plugins/shiki'

const plugins = [shiki()]
</script>

<template>
  <UChatMessages :messages="messages" :status="status">
    <template #content="{ message }">
      <template v-for="(part, index) in message.parts" :key="`${message.id}-${part.type}-${index}`">
        <UChatReasoning
          v-if="isReasoningUIPart(part)"
          :text="part.text"
          :streaming="isPartStreaming(part)"
        >
          <Markdown
            :value="part.text"
            :streaming="isPartStreaming(part)"
            :plugins="plugins"
            class="*:first:mt-0 *:last:mb-0"
          />
        </UChatReasoning>

        <template v-else-if="isTextUIPart(part)">
          <Markdown
            v-if="message.role === 'assistant'"
            :value="part.text"
            :streaming="isPartStreaming(part)"
            :plugins="plugins"
            class="*:first:mt-0 *:last:mb-0"
          />
          <p v-else-if="message.role === 'user'" class="whitespace-pre-wrap">
            {{ part.text }}
          </p>
        </template>
      </template>
    </template>
  </UChatMessages>
</template>
AI SDKの変更の詳細については、** official AI SDK migration guides **をご覧ください。
詳細な移行リファレンスについては、** AI SDK v7アップグレードPR **のすべての変更点を参照してください。