Nuxt UI reference implementation components built on useAISearch() composable. Provides drop-in ASK ai and chat interfaces using Nuxt UI primitives.
Requirements:
@nuxt/ui installed in your projectDrop-in container component providing full ASK ai and chat experience.
<template>
<!-- One line, fully featured -->
<AISearchContainer />
</template>
None - self-contained with internal useAISearch() instance.
None - all interactions handled internally.
<template>
<div>
<h1>Documentation Search</h1>
<AISearchContainer />
</div>
</template>
Toggle between search and chat modes.
modelValue'search' | 'chat'update:modelValue(value: 'search' | 'chat') => void<script setup lang="ts">
const ai = useAISearch()
</script>
<template>
<ModeToggle
:model-value="ai.mode.value"
@update:model-value="ai.switchMode"
/>
</template>
Search interface component (list mode).
querystringresultsSearchResult[]loadingbooleanerrorError | nullupdate:query(value: string) => voidsearch() => void<script setup lang="ts">
const ai = useAISearch({ mode: 'search' })
</script>
<template>
<SearchMode
v-model:query="ai.query.value"
:results="ai.searchResults.value"
:loading="ai.isLoading.value"
:error="ai.error.value"
@search="ai.search"
/>
</template>
Chat interface component (generate mode with streaming).
querystringmessagesMessage[]loadingbooleanerrorError | nullupdate:query(value: string) => voidsend() => voidcopy() => voidexport() => voidabort() => void<script setup lang="ts">
const ai = useAISearch({ mode: 'chat' })
</script>
<template>
<ChatMode
v-model:query="ai.query.value"
:messages="ai.messages.value"
:loading="ai.isLoading.value"
:error="ai.error.value"
@send="ai.sendMessage"
@copy="ai.copyAllMessages"
@export="ai.exportAsMarkdown"
@abort="ai.abort"
/>
</template>
Warning banner for ephemeral chat sessions.
None
copy() => voidexport() => void<script setup lang="ts">
const ai = useAISearch({ mode: 'chat' })
</script>
<template>
<SessionBanner
v-if="ai.messages.value.length"
@copy="ai.copyAllMessages"
@export="ai.exportAsMarkdown"
/>
</template>
Results list for search mode.
resultsSearchResult[]None
<script setup lang="ts">
const ai = useAISearch({ mode: 'search' })
</script>
<template>
<SearchResults
v-if="ai.searchResults.value.length"
:results="ai.searchResults.value"
/>
</template>
Message list for chat mode.
messagesMessage[]isStreamingbooleanNone
<script setup lang="ts">
const ai = useAISearch({ mode: 'chat' })
</script>
<template>
<ChatMessages
:messages="ai.messages.value"
:is-streaming="ai.isLoading.value"
/>
</template>
<script setup lang="ts">
const ai = useAISearch({ mode: 'search' })
</script>
<template>
<UContainer class="py-8">
<div class="space-y-6">
<!-- Mode Toggle -->
<div class="flex justify-center">
<ModeToggle
:model-value="ai.mode.value"
@update:model-value="ai.switchMode"
/>
</div>
<!-- Search Mode -->
<SearchMode
v-if="ai.mode.value === 'search'"
v-model:query="ai.query.value"
:results="ai.searchResults.value"
:loading="ai.isLoading.value"
:error="ai.error.value"
@search="ai.search"
/>
<!-- Chat Mode -->
<ChatMode
v-else
v-model:query="ai.query.value"
:messages="ai.messages.value"
:loading="ai.isLoading.value"
:error="ai.error.value"
@send="ai.sendMessage"
@copy="ai.copyAllMessages"
@export="ai.exportAsMarkdown"
@abort="ai.abort"
/>
</div>
</UContainer>
</template>
<template>
<UContainer class="py-8">
<h1>Documentation Search</h1>
<AISearchContainer />
</UContainer>
</template>
All components are fully typed. Import types from composables:
import type {
Message,
SearchResult,
UseAISearchOptions
} from '#ai-search/composables/useAISearch'
Components use Nuxt UI primitives and inherit your Nuxt UI theme configuration.
export default defineNuxtConfig({
ui: {
primary: 'green',
gray: 'slate'
}
})
<template>
<UContainer>
<SearchMode
v-model:query="query"
:results="results"
:loading="loading"
:error="error"
@search="search"
:ui="{
input: { size: '2xl' },
button: { color: 'primary' }
}"
/>
</UContainer>
</template>
All components follow Nuxt UI accessibility standards:
Same as Nuxt UI: