TanStack AI provides React hooks for streaming LLM responses in a way that integrates naturally with TanStack Query's caching and state model. It uses the Vercel AI SDK's provider interface under the hood, so pointing it at OpenDunes is identical to any other createOpenAI setup.
npm install @tanstack/react-ai @ai-sdk/openai
Create the OpenDunes provider and export it for use in your hooks.
import { createOpenAI } from "@ai-sdk/openai";
export const opendunes = createOpenAI({
baseURL: "https://opendunes.com/api/v1",
apiKey: process.env.OPENDUNES_API_KEY,
});
Note. The API key is a server-side secret. Use TanStack AI's server action or route handler pattern — never expose OPENDUNES_API_KEY to the browser.
"use server";
import { streamText } from "ai";
import { createStreamableValue } from "ai/rsc";
import { opendunes } from "@/lib/opendunes";
export async function chat(userMessage: string) {
const stream = createStreamableValue("");
(async () => {
const { textStream } = streamText({
model: opendunes("anthropic/claude-sonnet-5"),
messages: [{ role: "user", content: userMessage }],
});
for await (const chunk of textStream) {
stream.update(chunk);
}
stream.done();
})();
return { output: stream.value };
}
"use client";
import { useState, useTransition } from "react";
import { readStreamableValue } from "ai/rsc";
import { chat } from "@/app/actions/chat";
export function Chat() {
const [response, setResponse] = useState("");
const [input, setInput] = useState("");
const [isPending, startTransition] = useTransition();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setResponse("");
startTransition(async () => {
const { output } = await chat(input);
for await (const chunk of readStreamableValue(output)) {
setResponse((prev) => prev + (chunk ?? ""));
}
});
};
return (
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask something..."
/>
<button type="submit" disabled={isPending}>
{isPending ? "Thinking…" : "Send"}
</button>
{response && <p>{response}</p>}
</form>
);
}
Pass the full provider/model slug to opendunes("..."). Browse the catalog at /models.
opendunes("meta-llama/llama-4-maverick")
opendunes("anthropic/claude-opus-4.8")
OPENDUNES_API_KEY=sk-your-key-here