Recipe: ChatGPT-like UI
Build a streaming chat interface with Next.js, Tailwind CSS, and the Vercel AI SDK — all powered by Meridian.
Overview
This recipe walks through building a ChatGPT-style chat interface that streams responses token-by-token. You will use the Vercel AI SDK with Meridian as the model provider, giving you access to frontier models with zero infrastructure overhead.
What you will build
- A multi-turn chat interface with message history
- Streaming text responses rendered in real time
- Markdown rendering for code blocks and formatted output
- Conversation persistence via Meridian thread IDs
Prerequisites
- Node.js 18+ and a Next.js 14 project
- A Meridian API key — grab one from your dashboard
aiand@ai-sdk/openai-compatibleinstalled
Step 1 — Configure the provider
Create a provider instance pointing at the Meridian API. The OpenAI-compatible adapter works out of the box because Meridian implements the same chat completions contract.
// lib/meridian.ts
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
export const meridian = createOpenAICompatible({
name: "meridian",
baseURL: "https://api.getnimbus.net/v1",
headers: {
Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`,
},
});Step 2 — Build the API route
The route handler receives messages from the client, streams the model response, and returns it via the AI SDK stream protocol.
// app/api/chat/route.ts
import { streamText } from "ai";
import { meridian } from "@/lib/meridian";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: meridian("meridian-pro"),
messages,
});
return result.toDataStreamResponse();
}Step 3 — The chat component
Use the useChat hook from the AI SDK. It manages message state, handles submission, and streams the assistant response into the UI.
// components/chat.tsx
"use client";
import { useChat } from "ai/react";
export function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: "/api/chat",
});
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto">
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((m) => (
<div
key={m.id}
className={`p-4 rounded-xl ${
m.role === "user"
? "bg-violet-600/20 ml-12"
: "bg-zinc-800 mr-12"
}`}
>
{m.content}
</div>
))}
</div>
<form onSubmit={handleSubmit} className="p-4 border-t border-zinc-800">
<input
value={input}
onChange={handleInputChange}
placeholder="Message Meridian..."
className="w-full rounded-xl bg-zinc-800 px-4 py-3 text-white placeholder-zinc-500 outline-none ring-1 ring-zinc-700 focus:ring-[#8B5CF6]"
/>
</form>
</div>
);
}Step 4 — Wire it up
Import the Chat component into your page. That is it — you now have a fully streaming ChatGPT clone running on Meridian.
// app/page.tsx
import { Chat } from "@/components/chat";
export default function Home() {
return <Chat />;
}Going further
- Add system prompts by passing a
systemmessage as the first entry in the messages array. - Persist conversations by storing the thread ID returned in Meridian response headers.
- Enable function calling by defining tools in the
streamTextcall. - Swap
meridian-profor any model in the model catalog.
Need an API key?
Head to your dashboard and generate one in seconds. Free tier includes 1,000 requests per day.