v0, generating interfaces and the token cost
v0 is a Vercel tool that generates React components from a text description, using Tailwind and the shadcn/ui library. You can view the result live, refine it with follow up instructions, and copy it into your own project.
A sensible assessment starts with what it does not do. It does not replace architectural work, does not know your domain, and does not know what the rest of your application's screens look like. It shortens the path from an idea to a clickable layout, and that is concrete, measurable value as long as nobody expects a finished product from it.
Token billing and what it changes
The most important recent change concerns not generation but the bill.
Plans previously granted a set number of messages or generations, so cost was predictable by counting how many times a day somebody presses a button. Since a change Vercel announced in May 2025, billing follows input and output tokens converted into credits rather than a fixed message count. That means a single instruction costs whatever follows from its complexity and context size.
Three practical effects follow. A small spacing fix costs a fraction of generating a whole view, so minor iteration stopped being expensive. A long conversation about one project costs progressively more with each step, since the context grows. And: a monthly budget stopped being predictable from a generation count and needs watching.
Model choice adds to that. The variants are called v0 Mini, v0 Pro, v0 Max, and v0 Max Fast, and the gap in rate is wider than the naming suggests: a million output tokens costs $1.20 on the cheapest and $50 on the dearest. Small fixes are therefore worth dropping to a cheaper one while keeping the stronger one for a first draft of a complex screen. That is the same principle as working with models through an API: match the model to the step rather than to the whole process.
The plan layout surprises people, because a higher plan does not mean a larger allowance. On the price list as it stands in August 2026 the free plan grants $5 of credits a month and a seven message daily limit, the Plus plan at $30 per user a month grants $30 of credits, and the Business plan at $100 per user grants exactly the same $30 of credits. What separates the two paid plans is training opt out by default and company oriented conveniences rather than the generation limit. Above them sits custom pricing.
Worth knowing separately is what happens at the edge of the allowance, since that is the most often skipped part of the bill. Once credits run out, generation pauses until you buy more rather than quietly rolling onto the bill. Unused monthly credits carry over by one month on paid plans and accumulate no further, while credits bought outside the subscription expire after a year. The rates themselves shift along with the models, so check the vendor's current price list before a purchase decision anyway.
How to use it so it pays off
The biggest difference in results comes from what you supply as input rather than from how cleverly you phrase sentences.
A functional description works better than a visual one. "An orders table filtered by status and date, with an expandable row showing line items" gives a better result than "a nice table with a modern look", since the second sentence carries no information translatable into code.
A screenshot of an existing view works better still. The tool accepts images, so showing what the rest of the application looks like sets the style more effectively than a paragraph of adjectives.
The third thing is iterating in small steps. An instruction changing one thing yields a predictable result. An instruction changing five things usually breaks two of them, and working out which change was at fault costs more than doing them separately.
The fourth is supplying data rather than letting it be invented. Pasting the real shape of an object from your API makes the component operate on your fields rather than imagined ones, and removes the manual renaming afterwards.
What you get back
The code uses React with Tailwind and shadcn/ui components, the ones you copy into a repository rather than install as a dependency.
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
export function OrderCard({ number, status, amount }: Props) {
return (
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base">{number}</CardTitle>
<Badge variant={status === 'paid' ? 'default' : 'secondary'}>{status}</Badge>
</CardHeader>
<CardContent className="text-2xl font-semibold">{amount}</CardContent>
</Card>
)
}That is an advantage and a constraint at once. An advantage, since if your project runs on shadcn/ui and Tailwind, the generated code fits without translation. A constraint, since with another component library or your own visual system you receive code to rewrite, and the time saving melts away.
Check that before writing the tool into a team's process. On a project built with a different component set, v0 still works as a layout sketchpad, though not as a source of code entering the repository.
Limits worth knowing
A generated component looks complete, and that is its most misleading property.
Accessibility tends to be superficial. Elements looking like buttons are not always buttons, focus order can be accidental, and contrast gets chosen for aesthetics rather than legibility. Run it through a checking tool and a keyboard before it reaches users.
State and data are placeholders. The component renders a sample list, while wiring real data and handling loading, error, and empty results is work nobody did for you. On a real screen that part is often larger than the layout itself.
Behaviour on small screens needs verifying. Responsive classes are usually present, while whether a table of eight columns behaves sensibly on a phone gets settled in a browser rather than in a preview.
Consistency across screens is on you. Each generation largely starts fresh, so three views generated separately will carry three slightly different spacings, radii, and button heights. On one screen that is irrelevant; across twenty it becomes debt.
Moving the code into a project
The moment a generated view enters the repository decides whether the tool saved time or merely shifted the work.
Start by splitting it. A generated file usually holds the whole screen: layout, sample data, event handling, and styles in one place. Breaking it into a presentational component and a data fetching layer takes fifteen minutes, and without it the first requirements change means digging through a four hundred line file.
The split means the component receives ready data in props and does not know where it came from.
export function OrderList({ orders }: { orders: Order[] }) {
return (
<ul className="flex flex-col gap-2">
{orders.map((order) => (
<li key={order.id}>{order.number} — {order.status}</li>
))}
</ul>
)
}
export default async function Page() {
const orders = await fetchOrders()
return <OrderList orders={orders} />
}The gain only shows on the second change. A component taking data in props can be shown in a component catalogue, tested without a database, and reused on another screen, while a generated file with fetching inside suits none of those three.
The second thing is naming. The generator names things descriptively, often against your project's convention. Renaming while pasting costs a minute; left for later it stays forever, since nobody revisits working code to fix a name.
The third is dependencies. Generated code can reach for a shadcn/ui component you do not have yet, or an icon from a library you do not use. Check the import list before running it, since a missing dependency shows up as a build error while an unnecessary one simply grows the bundle.
The fourth is styles. Values written inline, particular greys or spacings outside your scale, look fine on one screen and drift on the tenth. Swapping them for project variables while pasting is cheap insurance.
The last point concerns Next.js. A generated component often arrives marked as a client component even when nothing in it requires that. Checking whether the directive is needed and removing it where it is not shrinks the code shipped to the browser with no other change.
v0 against the alternatives
| Tool | What it does | Where it runs | Pick it when |
|---|---|---|---|
| v0 | Generates components from a description | Browser, plus an API | A fast layout sketch in the Vercel stack |
| Cursor | Changes code in an existing project | Editor | Work on code you already have |
| Ready templates | Supplies finished layouts | Copy into the project | A standard screen, no waiting |
| A design in a graphics tool | Visual layer without code | A separate tool | You need sign off on looks before code |
The second row is the most common misunderstanding. These tools do not compete but serve different moments. v0 starts from nothing and produces a new view. An editor assistant works on what exists, sees the rest of the project, and maintains consistency that v0 by nature cannot see.
The sensible arrangement in practice looks like this: v0 for the first version of a screen that does not exist yet, then move the code into the project and continue in the editor. Trying to run all development inside a generating tool ends with the twentieth prompt fixing what the eighteenth broke.
The third row deserves honest consideration. A login form, a dashboard with a list and cards, or a pricing page exist in hundreds of ready variants, available immediately and free. Generating them from scratch can take longer than finding and adapting one. Collections such as TailGrids hand you whole sections in Tailwind classes to copy into your own project, though only part of the library is free under MIT and the rest is a one off purchase.
The API and programmatic use
Beyond working in a browser there is a programmatic interface, tied to paid plans. It lets you invoke generation from your own code, meaning you can build a feature where a user of your application describes a layout and you show them the result.
The call comes down to a few lines, since the vendor publishes its own library under a permissive licence. The key is read from an environment variable, so it never lands in the code.
import { v0 } from 'v0'
const result = await v0.chats.create({
message: 'An admin panel with an order list and a status filter',
})
if (result.error) {
throw new Error(result.error.message)
}
console.log('Chat created:', result.data.chat.id)The returned chat identifier matters more here than the code itself. It lets you return to the same thread and ask for a correction instead of regenerating everything, and under token billing that is a difference in the bill rather than merely in convenience.
That makes sense in a narrow set of cases: tools for content creators, template builders, internal generators of admin views. In a typical product application there is no point, since generating an interface at runtime solves a problem you do not have.
With such a deployment the cost needs computing separately and early. Token billing means the bill grows with the number of users touching that feature rather than with the number of your developers. For a feature available to everyone that is an entirely different scale from a team working in a browser.
Think through what happens to code generated on demand as well. Running code produced from a user's own description in that user's browser is safe as long as the code touches neither your data nor your session. Inside an isolated frame that is feasible; directly in the application usually not.
The isolation rests on two attributes here, the second of which gets skipped and matters more.
<iframe
src="https://preview.yourdomain.com/abc123"
sandbox="allow-scripts"
referrerpolicy="no-referrer"
></iframe>The first attribute strips the frame of everything but script execution, so the code cannot reach the parent page's cookies or local storage. What matters is what the list omits: without the same origin permission the frame stays a foreign origin, and adding it alongside script permission removes the isolation entirely. That is why a preview is better served from a separate domain than from a directory inside the application.
Rolling it out to a team
A few things reduce friction if the tool is to stay beyond one project.
Decide what enters the repository. Generated code goes through review exactly like hand written code rather than beside it. Without that rule the project gains a layer nobody read.
Build a shared starting point. A description of the stack, naming conventions, and data shapes, pasted at the start, produces more consistent results than everyone repeating the same thing in every instruction separately.
Guard the visual system. If you hold your own colour variables and spacing scale, state them in the description, otherwise you get the library defaults and somebody replaces them by hand after every generation.
The last item is expectations. The tool shortens the path to a first version rather than to a finished one. A team that does not settle this will sooner or later meet a screen that looks done and ship it without reviewing the edge states.
Common mistakes
The first is treating output as production code without review. It looks complete, while loading, error, and empty list states usually do not exist.
The second is holding a long conversation about one project instead of moving the code into a repository. Context grows, cost grows, and the effectiveness of fixes drops.
The third is generating from scratch what exists as ready templates. A standard screen is faster to find than to describe.
The fourth is skipping accessibility. Keyboard focus and contrast are two things worth checking every time, since a generator picks them for looks.
The fifth is five changes in one instruction. The result then has to be untangled, and that costs more than five separate steps.
The sixth is not watching consumption after the switch to token billing. The budget no longer follows the generation count, so without watching usage a month can surprise you.
The seventh is using the strongest model for everything. A spacing fix does not demand what a first draft of a complex view does, and the rate per million output tokens alone separates the cheapest and dearest variant by roughly forty times, for a result often indistinguishable.
FAQ
What does v0 cost?
The free plan grants $5 of credits a month and seven messages a day, while the paid plans cost $30 and $100 per user a month and both grant the same $30 of credits. Billing follows tokens consumed rather than the number of generations, so the bill depends on the chosen model and the context length. Once the allowance runs out, generation pauses until you buy more credits.
Is the generated code production ready?
As a starting point yes, as a finished product rarely. Layout and styles are usually fine, while handling loading, error, and empty states, accessibility, and wiring real data remain to be done.
Does v0 work outside the Vercel stack?
The code is ordinary React with Tailwind and shadcn/ui components, so it pastes into any project on that stack, not necessarily hosted on Vercel. With a different component library the output has to be rewritten and the time saving shrinks.
How does it differ from an editor assistant?
In when you use it. v0 creates a new view from nothing and cannot see the rest of the project. An editor assistant works on existing code, knows the project conventions, and maintains consistency, so it suits extending what already runs.
How do I get consistent looks across screens?
Supply the same description of the stack, colour variables, and spacing scale on every generation, and better still attach a screenshot of an existing screen. Each session largely starts fresh, so consistency will not appear by itself.
Documentation and the programmatic interface sit on the tool's site, and plan terms in the Vercel pricing.