Back to Docs
Recipe

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
  • ai and @ai-sdk/openai-compatible installed

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 system message 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 streamText call.
  • Swap meridian-pro for 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.