New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

@aibind/nuxt

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aibind/nuxt

AI SDK bindings for Nuxt — composables with defaults, server handlers, and agents

Source
npmnpm
Version
0.2.0
Version published
Weekly downloads
9
-18.18%
Maintainers
1
Weekly downloads
 
Created
Source

@aibind/nuxt

AI SDK bindings for Nuxt. Vue 3 composables, server handlers, and agents — all wired up with sensible defaults.

Features

🤏 Tiny — Ships only what you use. Tree-shakes per entry point. 🐇 Simple — Three composables: useStream, useStructuredStream, useAgent. Call in setup() and go. 🧙‍♀️ Elegant — Returns plain Ref<T> values. Use in templates or destructure freely. 🗃️ Highly customizable — Custom endpoints, custom fetch, per-request system overrides, named model registries. ⚛️ Reactive — Every piece of state is a Vue ref. Reactivity works exactly as you'd expect. 🔌 Batteries included — Server handler and default endpoints out of the box.

Install

npm install @aibind/nuxt ai vue

Peer dependencies: vue ^3.3, ai ^6.0.

Schema Libraries

useStructuredStream works with any Standard Schema-compatible library. Install one:

# Zod (v4 recommended — has built-in JSON Schema support)
npm install zod

# Valibot (requires JSON Schema converter)
npm install valibot @valibot/to-json-schema

# ArkType (built-in JSON Schema via .toJsonSchema())
npm install arktype

Quick Start

1. Add the server handler

// server/api/__aibind__/[...path].ts
import { createStreamHandler } from "@aibind/nuxt/server";
import { anthropic } from "@ai-sdk/anthropic";

const handler = createStreamHandler({
  model: anthropic("claude-sonnet-4-20250514"),
});

export default defineEventHandler(async (event) => {
  return handler(toWebRequest(event));
});

2. Stream in a component

<script setup lang="ts">
import { useStream } from "@aibind/nuxt";

const { text, loading, send } = useStream({
  system: "You are a helpful assistant.",
});

const prompt = ref("");
</script>

<template>
  <form @submit.prevent="send(prompt)">
    <input v-model="prompt" />
    <button :disabled="loading">Send</button>
  </form>

  <p v-if="text">{{ text }}</p>
</template>

Entry Points

@aibind/nuxt — Client Composables

import { useStream, useStructuredStream, defineModels } from "@aibind/nuxt";

defineModels(models)

Define named AI models for type-safe model selection.

// models.ts
import { defineModels } from "@aibind/nuxt";
import { anthropic } from "@ai-sdk/anthropic";

export const models = defineModels({
  default: anthropic("claude-sonnet-4-20250514"),
  fast: anthropic("claude-haiku-20250514"),
});

export type Models = typeof models.$infer; // 'default' | 'fast'

useStream(options?)

Reactive streaming text. Endpoint defaults to /api/__aibind__/stream.

const { text, loading, error, done, send, abort, retry } = useStream({
  model: "fast", // optional model key
  system: "You are a poet.",
  endpoint: "/api/custom/stream", // override default
  fetch: customFetch, // optional custom fetch
  onFinish: (text) => console.log(text),
  onError: (err) => console.error(err),
});

send("Write a haiku");
send("Now a limerick", { system: "Override system prompt" });
text.value; // reactive accumulated text
loading.value; // true while streaming
error.value; // Error | null
done.value; // true when complete
abort(); // cancel in-flight request
retry(); // re-send last prompt

useStructuredStream(options)

Streams JSON and parses partial objects as they arrive. Validates the final result with any Standard Schema-compatible library. Endpoint defaults to /api/__aibind__/structured.

import { useStructuredStream } from "@aibind/nuxt";
import { z } from "zod";

const { data, partial, raw, loading, error, send } = useStructuredStream({
  schema: z.object({
    sentiment: z.enum(["positive", "negative", "neutral"]),
    score: z.number(),
    topics: z.array(z.string()),
  }),
  system: "Analyze sentiment. Return JSON matching the schema.",
});

send("I love this product!");
partial.value; // Partial<T> — updates as JSON streams in
data.value; // T | null — fully validated after completion
raw.value; // raw JSON string

@aibind/nuxt/server — Server Handler

import { createStreamHandler, ServerAgent } from "@aibind/nuxt/server";

createStreamHandler(config)

Generic Web Request/Response handler for streaming endpoints.

const handler = createStreamHandler({
  model: anthropic("claude-sonnet-4-20250514"),
  prefix: "/api/__aibind__", // default
});

// Use in a Nuxt catch-all route:
export default defineEventHandler(async (event) => {
  return handler(toWebRequest(event));
});

Handles two routes:

  • POST {prefix}/stream — text streaming
  • POST {prefix}/structured — JSON streaming

ServerAgent

Server-side agent with tools, system prompt, and multi-step tool loops.

import { ServerAgent } from "@aibind/nuxt/server";
import { tool, stepCountIs } from "ai";
import { z } from "zod";

const agent = new ServerAgent({
  model: anthropic("claude-sonnet-4-20250514"),
  system: "You are a helpful assistant with access to tools.",
  tools: {
    get_weather: tool({
      description: "Get weather for a city",
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => ({
        city,
        temperature: "72°F",
        condition: "sunny",
      }),
    }),
  },
  stopWhen: stepCountIs(5),
});

@aibind/nuxt/agent — Client Agent

import { useAgent } from "@aibind/nuxt/agent";

useAgent(options?)

Reactive agent composable. Endpoint defaults to /api/__aibind__/agent.

<script setup lang="ts">
import { useAgent } from "@aibind/nuxt/agent";

const { messages, status, send, stop } = useAgent();

const prompt = ref("");
</script>

<template>
  <form
    @submit.prevent="
      send(prompt);
      prompt = '';
    "
  >
    <input v-model="prompt" />
    <button :disabled="status === 'running'">Send</button>
  </form>

  <div v-for="message in messages" :key="message.id" :class="message.role">
    {{ message.content }}
  </div>

  <button v-if="status === 'running'" @click="stop()">Stop</button>
</template>

Reactive refs:

  • messagesRef<AgentMessage[]>
  • statusRef<'idle' | 'running' | 'awaiting-approval' | 'error'>
  • errorRef<Error | null>
  • pendingApprovalRef<{ id, toolName, args } | null>

Methods:

  • send(prompt) — send a message, streams response incrementally
  • stop() — abort the current request
  • approve(id) / deny(id) — respond to tool approval requests

@aibind/nuxt/markdown — Streaming Markdown

import { StreamMarkdown } from "@aibind/nuxt/markdown";

Renders streaming markdown with recovery for unterminated syntax. Uses @aibind/markdown under the hood.

<script setup lang="ts">
import { useStream } from "@aibind/nuxt";
import { StreamMarkdown } from "@aibind/nuxt/markdown";

const { text, loading } = useStream({ system: "You are a helpful assistant." });
</script>

<template>
  <StreamMarkdown :text="text" :streaming="loading" />
</template>

Props:

  • text — markdown string to render
  • streaming — when true, applies markdown recovery (closes unterminated bold, code blocks, etc.)

@aibind/nuxt/history — Branching Conversation History

import {
  ReactiveChatHistory,
  ReactiveMessageTree,
  ChatHistory,
  MessageTree,
} from "@aibind/nuxt/history";

Tree-structured conversation history with branching support. Edit messages, regenerate responses, and navigate alternatives (ChatGPT-style).

<script setup lang="ts">
import { ReactiveChatHistory } from "@aibind/nuxt/history";

const chat = new ReactiveChatHistory<{ role: string; content: string }>();
chat.append({ role: "user", content: "Hello" });
chat.append({ role: "assistant", content: "Hi!" });
</script>

<template>
  <div v-for="(msg, i) in chat.messages.value" :key="chat.nodeIds.value[i]">
    {{ msg.role }}: {{ msg.content }}
  </div>
</template>

See @aibind/core README for full API documentation.

Requirements

  • Vue 3.3+
  • AI SDK 6.0+
  • Node.js 20+

License

MIT

Keywords

nuxt

FAQs

Package last updated on 04 Mar 2026

Related posts