How to build a multi-model AI chat app with v0
A chat interface is mostly one screen that has to feel right, which is the half v0 is best at. Describe it in the browser, get the thread and the composer looking correct quickly, then spend the rest of the build on the parts behind them: the streaming function on your own key, the privacy rules, and the ceiling.
v0
$ Build an AI chat app: the thread and composer first, then a server-side streaming function on my own provider key, conversations saved under my own sign-in, a model list defined once on the server, and a logged limit on every call.
- Chat screen right first
- Function and ceiling behind it
- Ready for you to review
What a multi-model chat app actually is
A text box, a list of replies, and a bill that grows every time somebody presses send. The first two take an afternoon. The third is what the rest of this page is about.
Building a basic chat box takes an afternoon. The real engineering goes into what happens behind the screen: managing API connections, preserving conversation history, and controlling costs.
At its core, a production-ready app handles four continuous tasks. It receives your input, streams the response in real time, saves conversation threads for later, and routes requests across different AI providers. The visual UI is just the demo everyone sees. Saving context and balancing vendor models is what makes the product truly usable.
What makes chat apps unique is that you pay for every single turn. Because AI models have no built-in memory, every new message re-sends the entire conversation history back to the API. A thread with forty messages costs roughly forty times more than the first turn. Without smart context management, your spending scales compounding with every response.
An AI chat app is essentially two products stitched together: the visual interface users see, and the billing logic that controls your API usage. Commercial SaaS tools charge high monthly seat fees to rent that second half. Building your own interface connects you directly to the API meter, giving you complete control over your margins and dramatically reducing overhead.
The thread is the product, not the text box
A single question and a single answer is a demo, and you can build one in an afternoon. What people come back for is the conversation still being there tomorrow, opening at the right message, carrying what was said before into what gets asked next, and being findable six weeks later when you half remember writing something down. Build the thread first and the text box becomes an input on it.
What you resend is what you pay for
Models have no memory between calls, so a chat app re-sends the conversation on every turn to create the impression of one. That is normal and it is also the entire cost model: the bill for message forty includes messages one to thirty-nine again. Deciding how much of the past to carry, and when to summarise it instead, is the difference between an app you leave running and one you switch off.
True “multi-model” architecture
Showing five AI models in a dropdown menu is easy. Connecting them is the hard part. Every provider uses different API formats, streaming logic, and error handling, so adding a new vendor takes a full week of coding, not just another line in a list. Don’t take model lists at face value. Always check what’s actually built under the hood.
What the meter actually reads
$0.15
The price of a million input tokens on gpt-4o-mini, the model this template falls back to, read on OpenAI’s own API pricing page in August 2026. Output runs $0.60. A ChatGPT Business seat is $25 a month billed monthly, so one seat-month buys roughly 166 million input tokens at that rate, whether the person holding it sends four messages or four hundred. The seat and the meter are not measuring the same thing, and that is the whole reason to own the interface.
developers.openai.com API pricing, read August 2026 · checked August 2026
The parts every chat app is built from
The first one is what everybody demonstrates. The other five are why anybody would still be signing in three months later.
A reply that arrives while it is written
Waiting eight seconds for a paragraph feels broken, and watching the same paragraph appear a word at a time feels fast, even though it took exactly as long. That is the biggest single difference between a chat app people use and one they close, and it is more work than it looks: the provider sends the answer in fragments over one long-running response, so your code has to read them as they arrive, cope with a fragment split down the middle, and put the whole thing on screen and into the database once it is done.
A thread that survives being left and come back to
The conversation belongs to an account, opens where it left off, and shows up in a list you can scan. The small things are what people notice: a title you can read in the sidebar, renaming, deleting, and an order that puts what you touched last at the top. None of it is hard, and all of it is the difference between a demo and something with your history in it.
A decision about how much past you carry
The model is given the conversation again on every turn, so the length of the thread is the price of the next answer. Left alone that grows without limit, which is fine at six messages and expensive at sixty. The choices are a window of recent messages, a running summary of the older ones, or a hard stop with a new thread. Pick one deliberately rather than finding out on a bill.
A picker that matches what is actually behind it
Offering several models means several vendors, and each has its own request format, streaming format and error shape. The honest options are an adapter per vendor, or one service that presents them all the same way. What you should not do is list a model you cannot reach, because the app will quietly answer with a different one and nobody looking at the screen will know.
A ceiling on a paid endpoint, not just a lock on it
Requiring a sign-in decides whose typing spends your money. It does not decide how much. You want a record of every call, a limit per person over a window you choose, and a total that stops the feature rather than emailing you about it. Watch the sign-up path too: an app that lets anybody create an account in one click has made the meter public, which is reasonable to do and unreasonable to do without a cap.
Rendering what a model actually sends back
Models answer in Markdown, so a raw reply is full of asterisks and backticks unless something turns them into headings, lists and code blocks. Code needs a monospace block with a copy button, because copying code out of a chat is most of what technical users do with one. It is a small feature that gets noticed the moment it is missing.
Own the interface or rent a seat on somebody else’s
The four products below meter three different things: a seat, an allowance of points, and a pool of credits. None of them meters the thing you are actually paying a provider for, which is tokens. One of them is a one-time licence that already does most of what this template does, and it is the row to read before you decide to build anything.
Build your own
Your own key means you pay the model’s list price per token and the bill tracks the typing. No seat, no allowance, and no monthly floor before the first message. It also means the product is yours: your name on it, your users signing in, and your decision about what to charge them.
- You pay the provider’s list price per token, with nobody’s margin on top
- Ten people cost no more than one, because there is no seat to buy
- Your own branding, your own sign-in, and your own users, which none of the four allows
- The conversations sit in your database, so searching, exporting and deleting are yours to define
- Which model answers is a setting you control rather than a catalogue somebody curates
- What a person may spend is a rule you write rather than an allowance you buy
Rent a chat product
ChatGPT Business · TypingMind · Poe · JumaWhat renting buys, and what this template genuinely does not have: a wide catalogue of models from several vendors, kept current by somebody else. Poe and Juma put many vendors behind one bill. TypingMind sells a polished client you point at your own keys. All four work the afternoon you pay for them.
- Many vendors’ models behind one bill, with new ones added as they land
- Nothing to run: no database, no functions, no deploy, and nobody to call at midnight
- Somebody else absorbs a provider changing an API or retiring a model
- A seat price or a points allowance is also a spending ceiling, and your own key has none until you write one
- Sign-in, history search and the day-to-day polish of a product with a full-time team behind it
- TypingMind in particular is a one-time licence on your own key, which is most of this template’s argument, already built
First because it is what everybody compares against, and because it prices the thing this section is about. The meter is the person: a seat costs the same in the month somebody sends four messages and the month they send four hundred, which is the arithmetic in the stat above. It is also single-vendor by construction, so the model list is OpenAI’s and the day a rival ships something better is a day you wait. What you get for it is the most polished product in this table by a distance, and for one person at $20 a month it is genuinely hard to argue with.
learn.chatgpt.com · checked August 2026
The most useful row in the table, and the one to be honest about: it is a working commercial product built on exactly the premise this guide argues for. You buy the licence once, plug in your own provider keys, and its own page notes that the price excludes "API costs". Multi-model parallel chats sit in the Premium tier. Its comparison column describes a ChatGPT or Claude subscription as "Single vendor only", which is the same point made from the other side. Read the $99 against the hours in the calculator above before you decide to build. The discount is a promotion rather than a list price, so check both figures when you look.
typingmind.com · checked August 2026
Included for what it does not tell you, which is worth as much as a price. The page served us yearly-billed figures with no tier names attached, so no month-to-month rate is quoted here. Nothing on that view says what a point buys either: different models cost different numbers of points per message, so "660 thousand points" cannot be turned into a number of conversations and any comparison you make with it is a guess. What Poe genuinely sells is breadth, many vendors’ models behind one subscription, which is the half this template does not have.
poe.com · checked August 2026
The row that agrees with us, which is why it is here. It is a team chat workspace across several vendors, and it has priced the seat away: credits are shared at the workspace level, and the page promises "no per-seat pricing ever" and "Unlimited seats on every plan". That is the same instinct as building your own, sold as a subscription, and for a team that wants it working on Monday it is a fair answer. Note the rename: team-gpt.com now redirects to juma.ai, which is a reminder to check a price rather than trust a screenshot.
juma.ai · checked August 2026
Rule of thumb, and it splits on what you want to own. If you want a better chat client for yourself or a small team, do not build this: TypingMind is $99 once on your own keys, and LibreChat is free, self-hosted and genuinely multi-vendor under an MIT licence. Both beat a from-scratch build on effort and on model breadth, and saying otherwise would be selling you something. What none of them gives you is a product with your name on it that your own customers sign in to and pay for. That is the case for building, and it is a real one: the moment other people are the users, a per-seat plan stops being a tool you buy and becomes a cost that grows with your success. Work out which of the two you are, because the answer is not close in either direction.
Why build with v0
v0 starts with the screens: forms, tables, dashboards, whatever the interface needs, generated in React and Next.js from a plain description. That’s the part most people notice first, because it’s fast and it looks finished immediately.
The backend isn’t automatic in the same way. v0 can write it too, meaning Next.js routes and server logic that read and write real data, but you ask for it, usually once the UI already exists and there’s something real to connect it to:
Sketch
Describe the screen or component you want.
Preview
See it rendered live, and select any part of it to adjust directly.
Connect
Add a database and the routes that read and write to it, once the UI needs somewhere real to save.
Iterate
Prompt again for the next screen or the next piece of logic.
The order matters here more than with a full-app builder: v0 gets you a finished-looking interface fast, and the data underneath doesn’t show up until you ask for it and give it somewhere to live.
One clickconnects a database
Supabase, Neon, and Upstash are integrations you add from the project menu once a screen needs to hold onto something real, and v0 provisions the credentials and writes the routes that use them.
Live preview is the feedback loop
Every prompt updates the working interface right there in the chat, so you see the actual screen changing instead of imagining it from a description.
Design mode edits without a prompt
Select an element in the live preview, adjust its style directly or type a plain-language instruction, and v0 applies the change back to the real source code as a new version.
Pay a developer, or do it with AI
Running costs for this app scale directly with how much your users chat. The estimates below cover development only. API usage is billed separately, so you will need to code your own spending caps to protect your budget.
Hire a developer
Custom build, from scratch- Developer
- ~$10k-$41k
- Supabase (backend)
- Free tier · $25/mo (Pro plan)*
- Hosting
- $0 free tier
- Model provider
- Per token - the line that moves with use
- Build time
- ~205 hrs of their work
~$10k-$41k to build, then from $25/mo plus what people type
Our ~205-hour estimate, priced at the rates in the survey linked below: senior US developers at $100-$150+ an hour, and agencies charging 20-40% above the freelancers they bid against, which is where $50/hr and $200/hr come from. Where those hours go is not where a quote usually assumes. The text box and the first reply are a couple of days. Streaming that survives a broken connection, a second vendor, a thread still cheap at message sixty, and a limit nobody can talk their way past are the rest. If a quote says "an AI chatbot", ask which of those four it covers.
Build it with v0
From scratch, with v0- v0
- $0 ($5/mo of credits) to $30+/month (Plus)
- Backend (Supabase)
- Free tier · $25/month (Pro plan)*
- Hosting (Vercel)
- Free on Vercel’s Hobby plan
- Your time
- ~98 hrs
Free to try the idea, ~$30+/month on Plus while you build a real one, then whichever plan you keep using
v0 bills in dollars of credit rather than a flat fee, so cost tracks usage: importing the pre-built template can fit inside Free’s $5 a month, while a from-scratch build runs into Free’s 7-messages-a-day cap quickly and usually needs Plus, at $30 per user per month with no annual discount shown. Because v0 generates the interface and the backend as separate steps rather than one pass, expect more prompts to reach the same result than a tool that writes both together.
* On this template the free-tier line to watch is the pause rather than storage. No files are kept, because attachments go to the model and are never saved, and a conversation is a few kilobytes of text. What does bite is that a free project sleeps after a week of inactivity, and an app people open when they happen to have a question is exactly the sort that goes quiet. Pro, from $25/mo, ends the pause and keeps a daily backup for 7 days.
Prices and rates from supabase.com, developex.com, v0.app, v0.app and vercel.com, checked August 2026.
Decide before you build
Six decisions to make before writing code. Half of them directly protect your API budget from unexpected user activity.
Should you support one AI provider or multiple?
Decide now whether a second provider is a real requirement or a nice line on a feature list. One vendor is a perfectly good product and half the work. If you do want two, choose between writing a custom adapter for each or routing everything through one service that presents them identically. The vendors section below compares both approaches.
What is your hard spending limit?
Read your provider’s per-token prices before you write a single line of code, then define your limits explicitly: how many API calls or tokens per person, over what time window, and what message a user sees when they reach that limit. Deciding this now means adding a single field on a table. Deciding it after your first surprising bill means rewriting the core function that everything runs through.
How much conversation history will you send back each turn?
The whole conversation history goes back to the model on every message unless you decide otherwise, so this is a cost decision disguised as a quality one. A rolling window of recent messages is cheapest, a running summary keeps more of the context, and having no limit at all is the expensive default you get by not making a choice. Pick one strategy now, because it shapes your database schema.
Who gets access, and how will you protect your API credit card?
A one-click demo account is excellent for showing the app off, but it also gives open access to your API credit card to anybody who finds the page. Decide whether you want open sign-up, email invitations, or a demo account that is capped much harder than a paid user account. Whatever you choose, usage limits must be set per account rather than per person, because accounts are free to create.
How long will you retain user conversation data?
Conversations accumulate quietly and they are the most personal data in this app. Decide upfront whether threads live forever, automatically expire after a set period, or can be permanently deleted on request. Then decide the same rule for file attachments, which in this template are sent to the model and never stored locally, and state this plainly to whoever signs in.
Are you building an internal tool or a commercial SaaS?
Building a tool for yourself and building a product for external customers are two completely different apps. Your own internal tool needs no branding, no billing system, and no legal terms. A product other people sign into needs all three, plus a clear explanation of what happens to the data they type. Decide which one you are making before designing the first screen, because it changes your database schema and authentication flow.
Comparing your build options
Building a basic chat interface is fast. The hard part is everything behind it: handling connection drops during streaming, managing token costs on long threads, and setting spending limits for users. Here are three ways to build the exact same product.
Getting one reply out of a model takes an afternoon. The months go on everything around it: a reply that arrives a word at a time and survives a dropped connection halfway through, a thread that still opens correctly on the fortieth message, a second vendor whose request shape and streaming format agree with the first one in your own code and nowhere else, and a limit on a button that spends real money every time somebody presses it.
A kit gives you a sidebar, a message list and a text box, which is the silhouette of this app and almost none of the work. Nothing in a kit knows how to stream, what to do when the stream stops mid-sentence, that you are billed for the entire conversation again on every turn, which vendor is answering, or that the person typing is signed in and still able to spend more of your money than you meant.
The interface arrives quickly and looking right, which is worth more on this build than on most: a chat app is judged almost entirely on one screen. Same task list as the rows above. Plan for the split, because the thread and the composer will be presentable long before the function behind them refuses an over-limit call.
Estimate your exact build timeframe
Customize your feature list below to see how build time changes. If you only need a single AI model or a basic prototype, uncheck unused items to reduce the estimate.
Your estimate
98 hrs
start to finish
Based on the 7 of 7 features you’ve selected, plus ~21h of groundwork. Toggle any on the left to watch the number move, and open the groundwork row to untick what you have already, such as a database that is already running or going live if you are only building a mock-up for now.
A rough estimate, not a quote. Real time depends on how much you customize and how clean your data is.
Let’s set up the tools you need
v0 runs entirely in the browser too, so there’s no download and no terminal. Before step 01: sign up, pick a plan, and know that a database is one click away once a screen actually needs to save something. GitHub is the last piece, and it’s what turns the chat into a project you can hand to someone else.
v0 account
Sign in with a GitHub, Google, or email account and you land in a chat where you describe the interface you want. There’s nothing to install.
Sign up for v0v0 subscription
Free includes $5 of credits a month, capped at 7 messages a day, which is fine for trying a few screens and thin for a real build. Plus is $30 per user per month, billed monthly only (v0’s pricing page shows no annual option), and it’s the realistic floor once you’re iterating past a handful of screens.
Compare v0 plansDatabase (Supabase, Neon or Upstash)
Where your project keeps its data, added when a screen needs one. v0 generates the interface first. A database is a one-click integration you add from the project menu once a screen needs somewhere real to save to, with Supabase, Neon and Upstash as the options, and connecting one lets v0 write the routes that use it.
Connect a database in v0GitHub connection
Not needed to build anything, since v0 keeps its own history of every change. Connect a repository from the chat’s Git panel and v0 also commits every code-changing message to its own branch, ready to merge as a pull request. That is the step that turns the chat into a project other tools, and other people, can use.
Connect v0 to GitHubThe database step is the one worth not skipping once your screens need real data. Everything else here takes a couple of minutes, and after that you’re just describing what you want.
Build your chat app, screen first
You describe what you want in the browser and the screen appears. The order below plays to that: get the thread and the composer looking right early, because a chat app lives or dies on one screen, then build the function behind them. Two things do not follow the screen. The ceiling goes in with the first real reply, and the model picker gets its own step at 05, because both are claims about code rather than things you can look at.
- 01
The chat screen, before anything is behind it
Start where this tool is strongest. A thread, a composer and a sidebar that look right, with sample messages in them and nothing real underneath yet.
PromptSet up the projectBuild the chat screen for an AI chat app in React with Tailwind, with sample data and no backend yet: a scrolling thread of messages with the person’s and the model’s messages styled differently, a composer at the bottom that grows as I type, a collapsible sidebar of past conversations, and a model name at the top. Make it work properly on a phone as well as a wide screen, and add a light and a dark theme. Then write down in the project notes that this app’s vocabulary is conversation, message, model, provider, stream, attachment, call log and limit, and that two rules apply to everything built after this: my provider key lives on the server and never reaches the browser, and every model call is logged and counted against a limit before it is made.
Spend a little longer here than feels necessary. This screen is the entire product from the outside, and it is much easier to get the composer and the message spacing right while there is nothing real behind them to break.
- 02
A database, sign-in, and one account out of another’s history
Now put something real underneath. The schema and the access rules together, because a conversation being private is the same idea as it having an owner.
PromptModel it and draw the boundaryAdd Supabase and set up the data model. Conversations with an owner, a title and a last-updated time. Messages belonging to a conversation, with the text, whether a person or the model wrote it, the model that answered, and attachment metadata. A calls table logging every model request: who, which conversation, which model, tokens in and out, whether it succeeded and an estimated cost. Then add email and password sign-in with a persisted session and a profile row created for a new account. Then row-level security on every table: a row is reachable only by the account it traces back to, with explicit WITH CHECK on every insert and update, and the update rule on a message must forbid changing which conversation it belongs to. Then wire the screen from step 01 to real data rather than samples, and prove the boundary: a second test account that cannot read or move the first one’s messages.
Ask for the proof as something you watch, not as a claim. It is the one part of this build with no visible symptom when it is wrong, and the screen will look exactly as correct either way.
- 03
The first streaming reply, and the ceiling with it
The screen already looks like it works. This is the step that makes it, and the limit belongs in the same request so there is never a version without one.
PromptStream one reply, with a ceilingBuild the chat call as a server-side function, with my provider key in the server environment and nowhere the browser can read it. It requires a signed-in user and refuses with a 401 otherwise, sends the conversation to the model, and streams the answer back to the browser as it arrives, rendering into the thread from step 01 as it comes. Do the streaming carefully: read the response in chunks, keep a buffer for a fragment split across two chunks rather than dropping it, and save the finished message when the stream ends, including when the connection drops halfway. Before it contacts the provider at all, write a row to the calls table and check two limits I can configure: calls per account in a rolling window, and a total across the app. Over either, return a clear "you have reached your limit for today" and do not call the provider. Read the limit and the cost on the server, never from the client. Then prove the limit refuses an eleventh call when I set the limit to ten.
The streaming and the ceiling are the two things on this page you cannot judge by looking at the result. Run the eleven-message test yourself, and watch a long reply arrive rather than trusting that it did.
- 04
Real conversations in the sidebar, and what you resend
The sidebar from step 01 gets its data, and the request gets a budget so a long thread does not cost more every turn.
PromptBuild the thread and the sidebarWire the sidebar to real conversations: ordered by what I touched last, opening one loads its messages in order, plus renaming, deleting and a new-conversation button. Give a conversation a title from the first exchange rather than from the first fifty characters of what I typed, and never overwrite a title I have renamed by hand. Then change what gets sent to the model: rather than the whole thread every turn, send the most recent messages up to a token budget I can configure and a short running summary of anything older, stored on the conversation and updated as messages fall out of the window. Keep the full history in the database. While I am testing, show the token count for the next request on screen.
The summary is the part to watch. A summary regenerated on every turn is itself a model call, which quietly undoes the saving, so check it is written when messages fall out of the window rather than each time you press send.
- 05
Make the picker tell the truth
The step this template needs more than most, and the one furthest from what a screen can tell you. A dropdown with five names is a visual component and a claim about five implementations.
PromptMake the picker honestDefine the list of models in one place on the server, with the vendor, the id and the per-token prices for each, and have the picker read that list rather than a separate one in the frontend. The function must refuse a model that is not in it with a clear message rather than silently substituting a default. Record on every message which model actually answered it and show that under the reply. Then, if I want a second vendor, add exactly one: a separate adapter for its request shape and its streaming format, behind the same function and the same ceiling, with its own key in the server environment. Tell me plainly which models are actually reachable and which are only names, and do not add a name I cannot reach.
This is the request where a good-looking answer is the risk. A dropdown is easy to render and a second provider adapter is a week of real work, so ask which of the two you have been given and expect the honest answer to be shorter than the list.
- 06Destination
Markdown, attachments, and one real conversation
The rendering the screen has been faking, the composer’s extras, your own name on it, and a rehearsal on the deployed copy.
PromptFinish it, then rehearseFinish the app. Render replies as Markdown with headings, lists and code in a monospace block with a copy button, since copying code out is most of what people do with a chat app. Add attachments to the composer, and tell me which trade you have taken: sending the file to the model without keeping it, which needs no storage, or storing it in a bucket with access rules and linking it from the message. Add dictation using the browser’s own speech recognition, with a sensible message in browsers that do not offer it rather than a dead button. Put my own name, colours and copy on the header and the sign-in page. Then deploy, and walk me through one real conversation on the deployed address: sign in as a second account, hold a conversation long enough to matter, confirm that account cannot see the first one’s threads, and compare what my calls table reports against my provider’s own usage page.
Do the rehearsal on the deployed address rather than the preview. This is the one build in the series where a limit that only works locally costs money rather than time to discover.
Protecting your app: authentication, security, and API costs
AI chat apps require protecting three things: sensitive user conversations, database privacy, and your API budget. Here are the core security rules to follow before launching.
Use your backend’s built-in authentication
Sign-up, sign-in, user sessions, and password resets should all come directly from the default authentication service attached to your database. Use it as it comes out of the box, because an AI chat app gives you no reason to build custom identity logic from scratch. A custom authentication system that is subtly broken will fail silently and give you no warning until data is leaked.
Require authentication for all AI API calls
This is the most important security pattern to copy from this template. The server function creates its database client directly from the user’s active session token, verifies who is making the request, and returns a 401 Unauthorized error if there is no logged-in user. An exposed API endpoint isn’t just a data privacy leak: it is an open credit card. If someone finds an unauthenticated endpoint, they can run automated scripts against it and leave you paying for every single API call. Check this rule first on any new feature you build.
Combine user login with hard spending limits
Authenticating users is only half the battle, and this part is up to you to implement. Requiring a login decides who is allowed to spend your money, but not how much they can spend. To protect your budget, you need a system that logs every API call, enforces usage quotas per account over a set timeframe, and hard-stops the feature when limits are reached instead of just sending you a warning email afterward. An unmetered paid endpoint is a critical financial vulnerability: treat it like a major security issue.
Set strict limits on public demo accounts
This is a specific version of the spending limit rule, and it is the one most developers miss. Creating a demo account without asking visitors to log in first is great for conversion on a landing page, but it means your sign-in barrier keeps out anonymous strangers while leaving the front door open to anyone else. Rate limits stored temporarily in server memory help, but they completely reset whenever a new instance of your server function starts up. You must attach spending caps directly to the account record in your database, not to temporary visitor sessions.
Keep your database ownership model simple
In this app architecture, every conversation belongs to a single user account, and every message belongs to a single conversation thread. This means every database access rule reduces to one simple check: "does this specific record trace back to the user making the request?" There are no complex user roles or shared workspaces here. This makes it the simplest security model possible and leaves no hidden edge cases where authorization bugs can hide.
Validate data constraints on record updates
This is a critical database rule to learn, and chat applications demonstrate it clearly. Every update rule in your database needs to answer two questions. The obvious first question is whether a user is allowed to edit a row at all. The often-forgotten second question is what that row is permitted to turn into. In a chat app, this prevents a vulnerability where a message could be edited to move it into a conversation thread owned by another user. Security rules must check both the existing column data on the way in and the updated data on the way out.
Restrict database read policies to prevent leaks
The development history of this template serves as a great lesson, which you can see in its database migration logs. It initially launched with a Row-Level Security (RLS) policy that accidentally allowed any logged-in user to read every row in the profiles table, including user email addresses. A later migration removed that rule and added a stricter one alongside another permissive policy, and a third migration finally removed that second policy too. It took two extra attempts to get a single read rule right on the simplest table in the app. Whenever you write a read rule that grants access to "anyone", check carefully which specific columns you are exposing.
Store API keys securely and disclose data usage
Your AI provider API key must live strictly in your server-side environment secrets and nowhere else. If you place an API key in client-side app code, anyone can extract it and spend your money. Beyond security, consider privacy: everything a user types is sent directly to an external AI provider. In a chat app, that text often includes sensitive personal notes, unreleased ideas, or client information that users wouldn’t share publicly. Decide what data is allowed to leave your app, state this clearly to users when they sign up, and read your AI provider’s privacy terms regarding how prompt data is handled.
Enable daily backups for user chat history
On a free database tier, there are no automated backups for your user data. Daily database backups stored for a week begin on paid plans like Supabase Pro ($25/mo). Having reliable backups is especially crucial for AI chat applications because users treat conversation threads like personal notebooks. If data is lost, users don’t just lose the last generated response, they lose weeks or months of accumulated history and context.
Review the access rules (row-level security policies) on every table. For each one, tell me in simple terms who can view, add, edit, and delete records, confirm that people can only reach their own data while the right roles can reach more, and flag anything left open that shouldn’t be.
Paste this into the chat before launch so v0 checks nobody can see data they shouldn’t.
One rule outranks everything else on this page: your database service key and your model provider key live on the server and nowhere else, which means never inside the app people download and never inside a repository. A key that escapes is burned whichever of the two it is, and the provider key is the one somebody will actually spend. Read your usage page first to see what was done with it, then replace it the same day.
What speeds the build, and what slows it
Speeds the build
- One screen per message, checked in the live preview before you ask for the next one
- Adding a database as soon as a screen needs to remember something, so v0 wires it up as it builds
- Clicking the thing you want changed in Design mode, rather than describing where it sits
- One feature per chat, so its history reads as a straight line you can walk back
- Sending a finished chat over to GitHub before starting the next feature, rather than stacking several together
Slows the build
- Asking for the whole app in one message instead of one screen at a time
- Building screens for data the database does not hold yet
- Describing which button to change when you could just click it
- Letting one chat run for weeks, so going back to an older version undoes everything built after it
- Approving several messages in a row without looking at the preview after each one
Connecting GitHub (Optional)
Every change in v0 is saved automatically without any technical setup. You only need to link GitHub if you want a private copy under your own control or plan to hand the codebase over to external developers.
Every message is a version
Each time a message changes your code, v0 saves it as a new version. Restoring an older one adds it back as the newest version instead of branching, so the history stays one straight line.
Versions, v0 docsUndo from the chat itself
Scroll back through the conversation and click the revert arrow on any earlier reply, or open the version number in the top right to jump straight to a specific one.
Connecting GitHub creates a real repository
From the chat’s Git panel, connect an existing repository or create one. v0 never writes straight to your main branch: every code-changing message is committed to its own working branch first.
GitHub integration, v0 docsA pull request is how it gets merged
When you’re ready, publish and open a pull request from that working branch into your base branch, review it like any other, and merge it. Starting a new chat picks up a fresh branch for the next round of changes.
This can also be v0’s deploy path
If the branch your pull requests merge into is also the linked Vercel project’s production branch, merging one triggers a production deployment. If it isn’t, the merge just follows whatever branch behavior that Vercel project is already configured with.
GitHub integration, v0 docsWhere to host your application
Hosting gives your app a home on the internet so anyone can open it via a web link. Choose a service below to make your site live. (Your database, logins, and business records are stored separately in Supabase, covered below).
| Host | Best for | Notes | Free tier |
|---|---|---|---|
| Vercel | One-click deploys | Point it at the repository and every push publishes itself, with nothing for a Vite project to configure. The question to settle before you invite anybody is which plan you belong on, because Hobby is licensed for personal, non-commercial use and a chat product your own customers sign in to is commercial by any reading. That puts you on Pro, at $20/user/mo. | Pro from $20/user/mo (Hobby is non-commercial) |
| Netlify | Drag-and-drop or Git | Connect the repository, or drag the built folder onto the page and be live inside a minute. The one setting that matters is the redirect rule it prompts you for, without which a link straight to a single conversation lands on a not-found page. | Free tier |
| Cloudflare Pages | Users in more than one country | The app is served from wherever the person opening it is. Worth a look if the people signing in are spread across time zones, because the first paint is the part they feel before the model has answered anything. | Generous free tier |
| GitHub Pages | Not really this app | Publishes from a GitHub project once one routing setting is changed. Here to be ruled out rather than chosen: free means a public repository, and this repository sits next to a function holding a key that strangers can spend. | Free from a public repo only |
| Firebase Hosting | Teams already on Google | A short setup, then one command per release. Nothing about a chat app argues for it either way. The argument is that your other accounts are Google already, and if you end up adding Gemini as a second vendor that is one supplier rather than two. | Free Spark tier |
| AWS Amplify Hosting | Teams already on AWS | Publishes from the AWS console, and wants the same rewrite rule as the others before a link to one conversation will resolve. Chosen because AWS is on the invoice already, never because the app asked for it. | Free tier (build + hosting) |
| Surge | Publish from the terminal | One command puts the built folder online with no repository involved. Fine for showing a colleague what the thread looks like. Wrong the moment real people sign in and start spending against your key. | Free - unlimited publishing |
| DigitalOcean App Platform | DigitalOcean users | Builds and serves from the account you already have, which in a small company means one fewer supplier to add to the list beside your database and your model provider. That list is already three long on this build. | Free - 3 static sites, 1 GB/mo transfer |
Speed is not what separates these eight, because all of them serve a built folder quickly enough. Three other questions do. Does the plan you are on allow commercial use, which Vercel’s Hobby tier does not. Can it publish from a private repository, given the function secrets sitting beside this code. And does a deep link resolve for a browser that has never seen your site, because a link to one conversation is exactly what somebody will paste to a colleague.
One thing to check on the day you go live, and it is not the hosting. Hold a conversation long enough for the reply to take a while, on a phone on mobile data rather than on your desk. Streaming runs over one long-lived response, and a proxy or a flaky connection that cuts it will leave a half-written answer on screen and a half-written row in your database. Decide what the app does with that before a user finds out for you.
Keep your data in Supabase
Your conversations and messages, the accounts they belong to, and the one function that calls the model, which is where the provider key belongs and the only place it should ever be.
| Service | Best for | Notes | Free tier |
|---|---|---|---|
| Supabase | Data, auth, and the model call | Conversations and messages live in Postgres, accounts come from its auth service, and an edge function makes the model call. That function matters more here than on most templates, because it is the only address at which the provider key and the spending ceiling can both sit. Getting started means opening a free project and giving the app its URL and publishable key, then putting the provider key into the function secrets, where the browser cannot reach it. File storage stays unused on this build, since attachments travel to the model rather than into a bucket. | Free tier, then usage-based |
How you reach a second vendor
The answer may well be that you do not, and that is a valid technical choice rather than a compromise. One provider is a complete product: this template ships that way, and most users never need more. Read this section only when you have decided you genuinely want to offer models from different providers in the same dropdown menu.
The cheapest per token, because nobody sits in the middle, and the most work per vendor. Each one needs its own key, its own request shape and its own streaming format handled in your function, which is roughly a week for the second and less for the third. Choose this when the list is short and you expect it to stay short.
developers.openai.com · checked August 2026
Listed separately because it is the specific second vendor this template’s picker already names and does not reach. Its free tier makes it cheap to try before you commit to the work, and its paid text price sits between the two OpenAI models most people use, so what a conversation costs starts to depend on which model somebody picked. Price them side by side before you let users choose freely.
ai.google.dev · checked August 2026
One request shape and one streaming format for every model, so a new vendor becomes a string in a list rather than a week of work. Read the fee correctly, because it is easy to misread as a tax on inference: it applies when you load credit, so $100 of credit costs $105.50. Against a week of building and maintaining an adapter that is cheap, and against staying on one provider you are happy with it is money for nothing.
openrouter.ai · checked August 2026
Do the arithmetic before you choose, because the fee is smaller than it looks. Take the conversations you expect in a month, multiply by the tokens a typical one sends, and remember that a thread resends itself on every turn. At gpt-4o-mini rates a thousand medium conversations a month is a bill in single-figure dollars, and 5.5% of that is cents. At that size the decision is about your own time rather than the fee, so the answer is usually the aggregator or nothing at all. The fee starts to matter at a volume where you would be negotiating with providers directly anyway. Figures read on each vendor’s own page in August 2026.
Where AI genuinely helps a chat app
The AI is the app here rather than an addition to it, so the first prompt below is not a feature. It is the guard rail the one model-calling function is missing. The four after it are things you would actually want next, and your provider key stays in that same function for all of them.
Log every call and put a ceiling on it
Nothing records what the model was asked or what it cost, so the expensive calls are the ones with no history. Build this before anything else below, because everything below spends money too.
Add a calls table that records every model call (who made it, which conversation, which model, how many tokens went in and came back, whether it succeeded, and a cost estimate) and write to it from the chat function on every call, successful or not. Then add a ceiling: a limit per account per rolling window and a total across the whole app, both settings I can change, checked inside the function before it contacts the provider and returning a clear "you have reached your limit for today" rather than an error. Apply a tighter limit to demo accounts than to real ones. Do not let the client send the limit or the cost, because both are read on the server. Then add a small usage panel showing calls and estimated spend for the current period, by account.
Stop paying for the whole thread on every message
The conversation is resent in full on every turn, so a long thread quietly gets expensive. This is the one change that makes a chat app cheaper to run without making it worse to use.
Change what gets sent to the model. Keep the most recent messages in full up to a token budget I can set, and for anything older send a running summary instead, stored on the conversation and updated when messages fall out of the window. Show me the token count for the next request in the interface while I am testing, so I can watch the effect. Keep the full history in the database untouched, because this is about what is sent rather than what is stored, and make the budget a setting rather than a number in the code.
Give a conversation a title worth scanning
A sidebar of threads named after the first sentence somebody typed is hard to search by eye, and it is the first thing that goes wrong once there are forty of them.
After the first exchange in a conversation, make one cheap model call to write a short title of at most six words describing what the conversation is about, and save it on the conversation. Do it once rather than on every message, let me rename it by hand afterwards and never overwrite a title I have edited myself, and count that call against the same log and ceiling as everything else. If the call fails, keep the current title rather than showing an error.
Ask before assuming somebody wants a picture
Deciding between a text answer and an image by looking for words like "image" or "draw" in the message is cheap and wrong often enough to notice, and it is wrong in the expensive direction.
Replace the keyword test that decides whether a message is an image request. Give me an explicit control in the composer for asking for an image, so the choice is mine and the app never guesses. Where you keep any automatic detection at all, make it a separate cheap classification call rather than a substring match, show me which mode a message was sent in, and let me resend the same message in the other mode without retyping it. Count image calls against the ceiling at their own higher cost rather than as one call.
Search your own history, and answer from it
After a few months the conversations know things you do not: what you asked before, what worked, and the answer you are now trying to reconstruct from memory.
Add search across my own conversations. Do the finding with database queries over the message text rather than with a model, show the matching messages with their conversation and date, and let me open one at the right place. Then add an optional panel that answers a question from my own history, sending only the messages the query actually matched and quoting which conversation each part of the answer came from. Never send my whole history in one request, and where the search finds nothing, say so instead of answering from general knowledge.
v0 doesn’t ship its own model for these prompts, so you bring an API key for whichever provider you want, and v0 writes the route that calls it. Start with a cheap model while you’re still iterating on the prompt itself, then swap in a stronger one for the version that ships, and keep every AI feature behind that one route so there’s only one key to rotate.
Get a head start with our template
Every route above begins at an empty folder, and there is one that does not. This app streams already, keeps every conversation under its own sign-in already, and renders what a model sends back already, so the weeks that the streaming, the thread and the accounts would have cost turn into an afternoon of adding your key and your name.
Multi-LLM Chat
The exact chat app this guide builds, packaged so you can open it, point it at your own backend, and make it yours from there. A multi-model chat app with streaming replies and saved conversation history. Switch between models like GPT-5, GPT-4o and Gemini 2.5, all running on your own provider key as a private, branded product.
The key benefits of starting with a template
Streaming replies, saved conversations, sign-in with a one-click demo, attachments, voice input, and Markdown rendering with code blocks. Read the security section above before you go live, and the fourth FAQ below for the one claim to check.
Building the core from scratch
~98 hrs
Opening the template, already built
~1 hr
~97 hrs of building you skip
Two deliberately different measurements. The build is the ~98 hrs. The hour is what adoption costs: a database of your own, a provider key in the function’s secrets, your name on the header, and one conversation held end to end to prove it works. Setting your own spending limit costs the same on either path, so neither figure includes it.
Streaming replies, handled properly
The reply arrives a word at a time over one long-lived response, and the code that reads it keeps a buffer for fragments that arrive split down the middle rather than dropping them. That buffering is the part a first attempt gets wrong, it produces answers with words missing, and it is already done here.
Conversations saved under your own sign-in
Email and password sign-in, a session that survives a refresh, and a sidebar of saved threads ordered by what you touched last, with renaming and deleting. There is also a one-click demo account for visitors, which is genuinely useful on a landing page and is the first thing to put a tighter limit on.
A composer that takes files and speech
Up to ten attachments at 20 MB each go to the model with your message, and dictation into the text box uses the browser’s own speech recognition, so it costs no tokens at all. Two honest limits: the browser dictation works in Chrome and Edge and largely does not in Firefox, and attachments are sent rather than stored, so the file name survives in the thread and the file itself does not.
Replies rendered the way models write them
Models answer in Markdown, and this renders it: headings, lists, and code in a monospace block rather than a paragraph full of backticks. Light and dark themes are both there, and it has been tested on real phones rather than only in a narrow browser window.
The provider key never reaches the browser
Every model call goes through one server-side function that checks for a valid session first and refuses without one, and your key lives in that function’s secrets. It handles the provider saying "slow down" gracefully too. What it does not do is cap anything, which is why the first prompt in the AI section above exists and why it is not optional.
One account per conversation, 11 live access policies
Every row traces back to the account that owns it, and 11 live row-level security policies out of 13 written decide who may read and change each table. Two were removed in later migrations, both of them read rules that had been too generous. There are no roles here, so everyone who signs in can do the same things, which is the right shape for a personal assistant and something to change deliberately if you add an admin.
From founders who build on our templates
We needed a live product in front of users fast. Instead of building from scratch, I grabbed this template, customized the logic with AI, and shipped in several days. What usually takes a dev team weeks took us a weekend.
Jeevan ThomasFounder & CEO, Hado.aiCommon questions
Only one, and this is the first thing to know before comparing it with anything. The model picker lists five names and one of them is Gemini 2.5 Flash, while the function behind it calls OpenAI and nothing else. Pick a model it does not recognise and it quietly answers with the default instead, which is currently gpt-4o-mini, with nothing on screen saying so. So what you are buying is a polished single-vendor chat app with a picker that is ready for more, rather than a working multi-vendor one. The vendors section above is the honest answer to what adding a second one costs, and the first build step in this guide has you make the picker tell the truth before you do anything else.
As it stands, yes, and it is the first thing to add. The model function is properly locked to signed-in users and handles the provider saying "slow down", so an anonymous stranger cannot call it directly. What is missing is any cap on somebody who is signed in: no log of calls, no quota, no rate limit, and no total. Two things make that sharper here than on most apps. Signing up is one click, because the app ships a demo-account function anybody can trigger. And a conversation resends itself to the model on every turn, so cost climbs as a thread gets longer. The first prompt in the AI section above adds the log and the ceiling together.
Whatever your provider charges for the tokens, and the answer that matters is the one you measure rather than the one you read. Two things to know before you look. Prices are per million tokens and differ by an order of magnitude between a small model and a large one, so which model somebody picks changes the bill more than how much they type. And the whole thread is sent again on every turn, so the fortieth message costs roughly forty times what the first one did. Send a realistic conversation with the model you plan to default to, then read your provider’s own usage page. That measurement is worth more than any figure quoted anywhere else.
No. Up to ten files at 20 MB each go to the model with your message, and only the name, size and type are kept on the message afterwards. Reopen the conversation tomorrow and you will see that a file called quarterly.pdf was attached and you will not be able to open it. That is a deliberate trade rather than an oversight, because it means the app needs no file storage and no rules over it, but it does mean "file attachments" promises less than a reader might assume. Keeping the files is a described addition: a storage bucket, access rules over it, and a link on the message.
Yes, through the same function, and the way it decides to is worth changing early. Rather than giving you a control, it looks at your message for words like image, picture, photo or draw, and sends anything containing one to the image endpoint instead of the chat model. So a question about optimising image loading in your code comes back as a picture, and image calls cost considerably more than text ones. The fourth prompt in the AI section above replaces the guess with a control you press on purpose.
It works where the browser supports it, which in practice means Chrome and Edge, and largely not Firefox. It uses the browser’s own speech recognition rather than sending audio to a model, which is why it costs nothing per use and why it is not consistent between browsers. Dictation that behaves the same everywhere means sending the audio to a transcription model instead, which is a described change and adds a per-minute cost to a feature that currently has none.
Several people, yes, and each one sees only their own conversations. Different permissions, no: this template has no roles at all, so everyone who signs in can do the same things. An administrator who can see usage, or a reviewer who may read a thread they did not write, is a described addition rather than a setting. Think about it before you add one, because a conversation is the most personal thing in this database and somebody who can read every thread is a decision worth making deliberately.
Yes, and that is the main reason to start from this rather than rent something. You get the full source under a licence that allows client work, so the name, the colours, the copy and the domain are yours to change. Charging your own users means adding billing, which this template does not include: a payment provider, a plan per account, and a link between the plan and the limits you set. Add the spending ceiling first, because a paid plan whose users can cost you more than they pay is a worse problem than no plan at all.
Three suppliers. A database, a host, and the model provider. The first two have free tiers you can stay on for a long time, because conversations are small rows of text and no files are stored. The third is the one that tracks how much people type and has no ceiling until you build one. If you upgrade anything first, make it Supabase Pro from $25/mo, and do it for the pause rather than the storage: a free project sleeps after a quiet week, and an app people open when a question happens to come up is exactly the sort that goes quiet.
Nothing here is proprietary. Conversations and messages sit in plain PostgreSQL tables that any Postgres host accepts from a standard dump, and the messages are ordinary text. The model calls go to your provider on your own key, so moving to a different vendor is a change in one function rather than a migration. The only thing that does not come with you is anything that was attached rather than typed, because attachments were never stored.
Yes, and every host listed above attaches one in a few clicks with HTTPS included. Do it before you invite anybody, because the domain is on every sign-in link and every password reset, so moving afterwards breaks those links for the people who already saved them.
No. You describe the screen you want in the chat and v0 generates the React and Next.js code behind it. Sign up, pick a plan, and connect a database once your screens need real data. The setup section above walks through each one.
Both, but not automatically at the same time. v0 generates the interface first. The backend (a database and the API routes that use it) is something you ask for once a screen actually needs to save or load real data.
Free plan credits reset monthly, and the 7-messages-a-day cap resets every day. If you hit either limit mid-build, what you’ve already built stays put. You wait for the reset or move up to Plus to keep going right away.
References
Sources checked August 2026- 01Pricing (plans, per-seat and per-user rates), ChatGPT docs. learn.chatgpt.com
- 02Pricing (one-time licence tiers, bulk licence, bring-your-own-key terms), TypingMind. typingmind.com
- 03Subscription plans (points allowances, yearly-billed rates), Poe. poe.com
- 04Pricing (workspace credits, seat policy), Juma. juma.ai
- 05API pricing (per-million-token rates by model), OpenAI. developers.openai.com
- 06Gemini API pricing (paid-tier rates, free tier), Google. ai.google.dev
- 07FAQ (markup policy, credit and bring-your-own-key fees), OpenRouter. openrouter.ai
- 08LICENSE (MIT), LibreChat. github.com
- 09Pricing (Pro plan, free-tier project pausing, backups), Supabase. supabase.com
- 10Web developer hourly rates 2026 (freelance and agency benchmarks). developex.com
- 11Row Level Security, Supabase docs. supabase.com
- 12Edge Functions secrets and environment variables, Supabase docs. supabase.com
- 13v0 homepage. v0.app
- 14Pricing (Plus, Business), v0. v0.app
- 15Pricing details (Free tier, credit rollover), v0 docs. v0.app
- 16Databases (connecting Supabase and others), v0 docs. v0.app
- 17Full-stack apps, v0 docs. v0.app
- 18GitHub integration, v0 docs. v0.app
- 19Deployments, v0 docs. v0.app
- 20Design mode, v0 docs. v0.app
- 21Versions, v0 docs. v0.app
- 22Pricing (Hobby plan), Vercel. vercel.com
This guide is general information, not legal advice. What a model provider may do with what you send it, and what you must tell the people signing in to your app about where their conversations go, are set by that provider’s terms and by your own local rules, so read both before other people type anything into what you build. Third-party prices, plan terms, and market rates are quoted from the sources above and were last checked on the date shown. Vendors change them without notice, and model prices move faster than anything else here, so confirm before you budget. Build hours and the cost estimates derived from them are our own estimates, not quotes. v0 is a product of Vercel. Verify current capabilities and pricing before relying on them.