All skills
onmax avatar

/nuxt-ui

@de09c7b official
by Maxonmax/nuxt-skills715 stars
38

Build UIs with @nuxt/ui v4 — 125+ accessible Vue components with Tailwind CSS theming. Use when creating interfaces, customizing themes to match a brand, building forms, or composing layouts like dashboards, docs sites, and chat interfaces.

Use this Skill: https://skilld.dev/gh/onmax/nuxt-skills/nuxt-ui

This session only. Nothing lands on disk.

referenceslayoutschat.md

≈1.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Chat Layout

Build AI chat interfaces with message streams, reasoning, tool calling, and Vercel AI SDK integration.

When to use

  • AI chatbot interfaces
  • Customer support chat
  • Any conversational UI with streaming responses

Setup

Install dependencies

Nuxt:

pnpm add ai @ai-sdk/gateway @ai-sdk/vue @comark/nuxt

Vue (Vite):

pnpm add ai @ai-sdk/gateway @ai-sdk/vue @comark/vue

Register Comark module

Nuxt:

export default defineNuxtConfig({
  modules: [
    '@nuxt/ui',
    '@comark/nuxt'
  ]
})

Vue (Vite): No module registration needed, import directly from @comark/vue.

@comark/nuxt (or @comark/vue for Vue projects) provides the Comark component used to render AI responses as streaming Markdown, it incrementally renders tokens as they arrive and automatically enables Nuxt UI's prose components.

Dark mode for syntax highlighting

When using the highlight plugin, add the following CSS to your stylesheet:

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;
}

Server endpoint

Using Vercel AI Gateway (recommended):

import { streamText, convertToModelMessages, toUIMessageStream, createUIMessageStreamResponse } from 'ai'
import { gateway } from '@ai-sdk/gateway'

export default defineEventHandler(async (event) => {
  const { messages } = await readBody(event)

  const result = streamText({
    model: gateway('anthropic/claude-sonnet-5'),
    instructions: 'You are a helpful assistant.',
    messages: await convertToModelMessages(messages)
  })

  const stream = toUIMessageStream({ stream: result.stream })
  return createUIMessageStreamResponse({ stream })
})

Or with a direct provider (e.g., pnpm add @ai-sdk/openai):

import { streamText, convertToModelMessages, toUIMessageStream, createUIMessageStreamResponse } from 'ai'
import { openai } from '@ai-sdk/openai'

export default defineEventHandler(async (event) => {
  const { messages } = await readBody(event)

  const result = streamText({
    model: openai('gpt-5-nano'),
    instructions: 'You are a helpful assistant.',
    messages: await convertToModelMessages(messages)
  })

  const stream = toUIMessageStream({ stream: result.stream })
  return createUIMessageStreamResponse({ stream })
})

Component tree

UDashboardPanel
├── #header → UDashboardNavbar
├── #body → UContainer → UChatMessages
│                         ├── #content → UChatReasoning, UChatTool, Comark
│                         └── #indicator (loading)
└── #footer → UContainer → UChatPrompt
                            └── UChatPromptSubmit

Full page chat

<script setup lang="ts">
import { isReasoningUIPart, isTextUIPart, isToolUIPart, getToolName } from 'ai'
import { useChat } from '@ai-sdk/vue'
import { isPartStreaming, isToolStreaming } from '@nuxt/ui/utils/ai'
import highlight from '@comark/nuxt/plugins/highlight'

definePageMeta({ layout: 'dashboard' })

const input = ref('')

const { messages, status, error, sendMessage, stop, regenerate } = useChat({
  onError(error) {
    console.error(error)
  }
})

function onSubmit() {
  if (!input.value.trim()) return
  sendMessage({ text: input.value })
  input.value = ''
}
</script>

<template>
  <UDashboardPanel>
    <template #header>
      <UDashboardNavbar title="Chat" />
    </template>

    <template #body>
      <UContainer>
        <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)"
              >
                <Comark
                  :markdown="part.text"
                  :streaming="isPartStreaming(part)"
                  :plugins="[highlight()]"
                  class="*:first:mt-0 *:last:mb-0"
                />
              </UChatReasoning>

              <UChatTool
                v-else-if="isToolUIPart(part)"
                :text="getToolName(part)"
                :streaming="isToolStreaming(part)"
              />

              <template v-else-if="isTextUIPart(part)">
                <Comark
                  v-if="message.role === 'assistant'"
                  :markdown="part.text"
                  :streaming="isPartStreaming(part)"
                  :plugins="[highlight()]"
                  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>
      </UContainer>
    </template>

    <template #footer>
      <UContainer class="pb-4 sm:pb-6">
        <UChatPrompt v-model="input" :error="error" @submit="onSubmit">
          <UChatPromptSubmit :status="status" @stop="stop()" @reload="regenerate()" />
        </UChatPrompt>
      </UContainer>
    </template>
  </UDashboardPanel>
</template>

Key components

  • UChatMessages — scrollable message list with auto-scroll. Props: messages, status. Slots: #content (per message), #actions, #indicator.
  • UChatMessage — individual bubble. Props: message, side ('left'/'right').
  • UChatReasoning — collapsible reasoning block. Auto-opens during streaming, auto-closes when done. Use isPartStreaming(part) from @nuxt/ui/utils/ai.
  • UChatTool — tool invocation status. Use isToolStreaming(part). Variants: 'inline' (default), 'card'.
  • UChatPrompt — enhanced textarea for chat. Its typed API exposes a selected subset of UTextarea props, forwards additional attributes to the underlying textarea, and adds chat-specific props such as error and submitOnEnter.
  • UChatPromptSubmit — submit button with automatic status handling (send/stop/reload).
  • UChatPalette — layout wrapper for chat inside overlays.

Chat in a modal

<UModal v-model:open="isOpen">
  <template #content>
    <UChatPalette>
      <UChatMessages :messages="messages" :status="status" />

      <template #prompt>
        <UChatPrompt v-model="input" @submit="onSubmit">
          <UChatPromptSubmit :status="status" />
        </UChatPrompt>
      </template>
    </UChatPalette>
  </template>
</UModal>

With model selector

<UChatPrompt v-model="input" @submit="onSubmit">
  <UChatPromptSubmit :status="status" />

  <template #footer>
    <USelect
      v-model="model"
      :icon="models.find(m => m.value === model)?.icon"
      placeholder="Select a model"
      variant="ghost"
      :items="models"
    />
  </template>
</UChatPrompt>

Conversation sidebar

Combine with dashboard layout for a ChatGPT-like interface:

<template>
  <UDashboardGroup>
    <UDashboardSidebar collapsible resizable>
      <template #header>
        <UButton icon="i-lucide-plus" label="New chat" block />
      </template>

      <template #default>
        <UNavigationMenu :items="conversations" orientation="vertical" />
      </template>
    </UDashboardSidebar>

    <slot />
  </UDashboardGroup>
</template>

Source: SKILL.md on GitHub

No alerts12d5 checks · Risk SAFE
  • Gen Agent Trust Hub12d

    The skill provides comprehensive documentation and implementation guidance for the Nuxt UI v4 component library. It includes a maintenance script for synchronizing documentation with the official repository. No malicious patterns or security risks were identified.

  • Socket12d

    No alerts

  • Snyk12d

    Risk: LOW · No issues

  • Runlayer7mo

    129 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at de09c7b. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated last week

README badge

README badge for onmax/nuxt-skills/nuxt-ui

Builds styled UI for Nuxt 4+ using @nuxt/ui v4 components with Tailwind CSS and Tailwind Variants. Covers forms with validation, data tables, modals, and theme customization through semantic colors and CSS variables.

Generated from the current SKILL.md.

Does this skill cover Reka UI headless primitives?
No. This skill focuses on @nuxt/ui v4 components (styled, opinionated). For Reka UI primitives, accessibility patterns, and asChild behavior, use the reka-ui skill instead.
What form validation libraries does this support?
Zod and Valibot. The skill includes guidance on useFormField and form component integration with both libraries.
Do I need UApp wrapper?
Yes, UApp is required to use Toast, Tooltip, and overlay components like Modal and Slideover.
Can I customize colors and theme?
Yes. The skill covers semantic colors, CSS variables, app.config.ts setup, and Tailwind Variants for type-safe styling.
What Nuxt and Vue versions does this require?
Vue 3 and Nuxt 4+. The current stable version is v4.4.0 (January 2026).

Generated from the current SKILL.md. These answers refresh after source changes.