vue-llm
AI/LLM composables for Vue — useChat, useCompletion, streaming, multi-provider. The missing AI SDK for Vue.
| Install | |
|---|---|
npm install vue-llm |
|
| Latest Version: | 0.5.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 25, 2026 |
| Links: | GitHub · npm |
Maintainer: dadashi123
vue-llm
AI/LLM composables for Vue. The missing AI SDK for the Vue ecosystem.
useChat() with streaming, tool calling, multi-provider support — in 10 lines of code.
Features
useChat()— reactive composable with streaming, abort, regenerate- Multi-provider — OpenAI, Anthropic, Ollama out of the box
- Tool calling — automatic roundtrips with
onToolCallhandler - Streaming — SSE parser, text/reasoning/tool-call deltas
- Security —
dangerouslyAllowBrowserguard protects API keys - Nuxt module — auto-imports, server-side runtime config
- Zero runtime deps — only
vueas peer dependency - TypeScript — full type safety, exported types
Quick Start
npm install vue-llm
<script setup>
import { useChat } from 'vue-llm'
import { createOpenAI } from 'vue-llm/providers'
const ai = createOpenAI({
apiKey: 'sk-...',
dangerouslyAllowBrowser: true, // only for prototyping
})
const { messages, input, handleSubmit, isLoading } = useChat({
provider: ai,
model: 'gpt-4o',
})
</script>
<template>
<div v-for="msg in messages" :key="msg.id">
<strong>{{ msg.role }}:</strong>
<span v-for="(part, i) in msg.parts" :key="i">
<template v-if="part.type === 'text'">{{ part.text }}</template>
</span>
</div>
<form @submit.prevent="handleSubmit">
<input v-model="input" placeholder="Ask something..." />
<button :disabled="isLoading">Send</button>
</form>
</template>
Providers
OpenAI
import { createOpenAI } from 'vue-llm/providers'
const ai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4o', // default model
})
Anthropic
import { createAnthropic } from 'vue-llm/providers'
const ai = createAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
})
const { messages, sendMessage } = useChat({
provider: ai,
model: 'claude-sonnet-4-20250514',
})
Ollama (local)
import { createOllama } from 'vue-llm/providers'
const ai = createOllama() // localhost:11434 by default
const { messages, sendMessage } = useChat({
provider: ai,
model: 'llama3',
})
Tool Calling
const { messages, sendMessage } = useChat({
provider: ai,
model: 'gpt-4o',
tools: [
{
name: 'get_weather',
description: 'Get weather for a city',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' },
},
required: ['city'],
},
},
],
onToolCall: async ({ toolName, args }) => {
if (toolName === 'get_weather') {
return { temperature: 22, condition: 'sunny' }
}
},
maxToolRoundtrips: 3,
})
System Prompt
const { messages, sendMessage } = useChat({
provider: ai,
model: 'gpt-4o',
system: 'You are a helpful assistant that speaks like a pirate.',
})
API Reference
useChat(options)
Returns a reactive chat interface.
Options:
| Option | Type | Description |
|---|---|---|
provider |
LLMProvider |
Provider instance (required) |
model |
string |
Model ID (required) |
system |
string |
System prompt |
tools |
ToolDefinition[] |
Available tools |
onToolCall |
(call) => unknown |
Tool call handler |
onFinish |
(msg, ctx) => void |
Called when response completes |
onError |
(error) => void |
Error handler |
maxToolRoundtrips |
number |
Max tool call rounds (default: 5) |
Returns:
| Property | Type | Description |
|---|---|---|
messages |
Ref<UIMessage[]> |
Chat messages |
status |
Ref<ChatStatus> |
'ready' | 'submitted' | 'streaming' | 'error' |
error |
Ref<Error | undefined> |
Last error |
input |
Ref<string> |
Input text (v-model) |
isLoading |
ComputedRef<boolean> |
Whether a request is active |
sendMessage |
(text?) => Promise |
Send a message |
stop |
() => void |
Abort current stream |
regenerate |
() => Promise |
Re-send last user message |
setMessages |
(msgs) => void |
Replace message history |
handleSubmit |
() => Promise |
Form submit handler (sends input.value) |
clearError |
() => void |
Clear error state |
Nuxt Module
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['vue-llm/nuxt'],
runtimeConfig: {
vueLlm: {
openaiApiKey: '',
anthropicApiKey: '',
},
},
})
Composables and provider factories are auto-imported.
Security
Providers throw an error if used in the browser without explicit opt-in:
// This will throw in browser:
const ai = createOpenAI({ apiKey: 'sk-...' })
// Opt-in for prototyping only:
const ai = createOpenAI({
apiKey: 'sk-...',
dangerouslyAllowBrowser: true,
})
For production, proxy API calls through your server or use the Nuxt module with runtimeConfig.
Message Format
Messages use a parts-based format for rich content:
interface UIMessage {
id: string
role: 'user' | 'assistant'
parts: UIMessagePart[]
}
type UIMessagePart =
| { type: 'text'; text: string; state: 'streaming' | 'done' }
| { type: 'reasoning'; text: string; state: 'streaming' | 'done' }
| { type: 'tool-call'; toolCallId: string; toolName: string; args: unknown; state: string }
License
MIT
Related Packages
nuxt-auto-translate
Automatic i18n translations for Nuxt using LLMs (OpenAI, Anthropic, Gemini, Groq...
2,009
2
@agentskit/vue
Vue 3 composable + headless chat components for AgentsKit — streaming, tools, an...
6,400
37
@aibind/nuxt
AI SDK bindings for Nuxt — composables with defaults, server handlers, and agent...
1,296
19
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.5.0 | ^3.3.0 | MIT | |
| 0.4.0 | ^3.3.0 | MIT | |
| 0.3.0 | ^3.3.0 | MIT | |
| 0.2.1 | ^3.3.0 | MIT | |
| 0.2.0 | ^3.3.0 | MIT | |
| 0.1.5 | ^3.3.0 | MIT |
Pre-releases (1)
| Version | Released | License |
|---|---|---|
| 0.0.0-stage |