← AI tools
v0

v0.dev

v0 is prompt-driven and doesn't run MCP servers — so the trick is feeding it your project's llms.txt as up-front context. Then it generates components that fetch from real endpoints with the right shape and auth header.

Overview

v0 is a code-and-design playground for generating React/Next.js UIs from prompts. It doesn't execute MCP tools or run shell commands — everything happens inside the prompt loop. That means it can't change your schema for you, but it can absolutely write a feed screen, a sign-in form or an admin table that hits your real chisel.to endpoints — if you give it the right context up front.

The trick is the llms.txt brief on your project's Connect page. It's a focused markdown document listing your base URL, auth conventions, every resource and field. Pasted into the first message of a v0 chat, it sticks for the whole conversation and v0 stops inventing endpoints.

v0 won't keep that context across separate chats. Treat each new prompt session as starting from scratch — repaste llms.txt at the top of each one.

Requirements

  • A v0.dev account.
  • A chisel.to project with at least one publishable API key.
  • Your project's llms.txt downloaded from the Connect page.

That's it. No installs, no env variables, no MCP bridge.

Get the llms.txt brief

  1. Open your project in the chisel.to dashboard.
  2. Open the Connect card.
  3. Click Download llms.txt (or use the copy button if you'd rather paste directly without saving).

The file is markdown — readable as-is, designed to be ingested as prompt context. It contains:

  • The project's API base URL.
  • How authentication works (API key as a Bearer token, user JWTs from /auth/login).
  • Response shape conventions (the { data, meta } envelope, pagination, filter/sort syntax).
  • Every resource with its column names and which CRUD operations are exposed.
  • Platform endpoints (auth, files, email, messages, payments).

The prompt template

The single most important pattern with v0 is leading every prompt with a clear framing line, then the brief, then the actual request. Use this template:

prompt — start of each v0 chat
Use the backend described below for all data. Call the API from a client component
with fetch, sending the API key in the Authorization header. Do not invent
endpoints, columns or response shapes — only use what's in the brief.

<<< llms.txt
[paste the entire contents of llms.txt here]
>>>

Now: [your actual request]

Three things make this work:

  • The framing line. "Do not invent endpoints" is the single most important sentence in a v0 prompt. Without it, v0 will sometimes paper over gaps by inventing routes that look reasonable but don't exist.
  • The delimiter. Marking the brief with <<< / >>> (or any obvious bracket) helps v0 treat it as data, not as instructions to interpret.
  • The trailing request. Always end with the specific UI you want generated. v0 acts on the last instruction more than the middle of the prompt.

Verify it works

First prompt to confirm v0 has the brief loaded:

prompt
[paste the template above]

Now: list every resource I described in the brief, with their column names.
Don't generate any UI yet.

If v0 returns your real resources, you're set. If it lists something generic ("posts, users, comments") that doesn't match your project, scroll up and check that you actually pasted the brief — it's easy to lose with a misplaced delimiter.

Common workflows

1. Generate a feed screen

prompt
[template + brief]

Now: build a Next.js client component for /feed that lists posts with
status=published, sorted by created_at descending, 20 per page. Each card shows
the title, the truncated meta_description, the author name (look up from authors
by author_id), and a published_at relative timestamp. Use Tailwind. Cache the
result with SWR.

2. Build a sign-up + sign-in pair

prompt
[template + brief]

Now: generate two screens, /sign-up and /sign-in, both as Next.js client
components. Use the project's auth endpoints. On success, store the access_token
in memory (not localStorage) and the refresh_token in an httpOnly cookie via a
server action. Show inline field errors when the API returns 422.

3. Generate an admin table with column-aware filters

prompt
[template + brief]

Now: build /admin/posts. Render the posts list as a sortable table. The filter
bar should be derived from the schema — status becomes a dropdown of the
allowed enum values; views and created_at become range filters. Inline-edit
mutable columns. Show a "publish" button that calls the custom action.

4. Compose against a custom endpoint

Custom endpoints show up in llms.txt under their parent resource. v0 will use them if asked:

prompt
[template + brief]

Now: on /trending, render the result of the custom query "popular" on posts.
Top 10 results in a grid of cards. Add a tab strip so a user can switch between
"This week", "This month" and "All time" — pass the corresponding window param
to the popular query.

5. Generate a small SDK wrapper

prompt
[template + brief]

Now: generate a TypeScript file (api/chisel.ts) that wraps the fetch calls for
this project. Export one function per common operation: listPosts, getPost,
createPost, signIn, signOut, uploadFile. Each takes a typed argument and returns
a typed result. No external dependencies; just fetch + types.

Iterating inside the same chat

Within a single v0 chat, the brief is "remembered" because it's earlier in the same conversation. Follow-up prompts can be terse:

prompt — follow-up
Now make the post cards expand on tap to show the full body. Animate with
framer-motion. Keep everything else the same.

Two pitfalls when iterating:

  • Long chats drift. After ~20 messages, the brief is far enough back that v0 sometimes forgets a column exists. Repaste the brief whenever the model seems to lose track.
  • "Make it pretty" overrides correctness. If a styling request changes the data structure ("group these by author"), the result might suddenly call a non-existent endpoint. Always specify the data shape explicitly even on styling iterations.

Wire the generated code into a real project

v0 generates self-contained components that paste into a Next.js (or any React) app. Two patterns make integration cleaner:

Per-project API helper

Centralize the base URL and the auth header in one helper. Then prompt v0 to use that helper instead of re-inlining fetch in every component.

ts — src/lib/chisel.ts
const BASE = process.env.NEXT_PUBLIC_CHISEL_URL!;
const KEY  = process.env.NEXT_PUBLIC_CHISEL_KEY!;

export async function chisel(path: string, init: RequestInit = {}) {
  const res = await fetch(`${BASE}${path}`, {
    ...init,
    headers: {
      ...init.headers,
      Authorization: `Bearer ${KEY}`,
      Accept: "application/json",
    },
  });
  if (!res.ok) throw new Error(`chisel ${res.status}: ${await res.text()}`);
  return res.status === 204 ? null : res.json();
}

Then prompt v0:

prompt
Use the helper from src/lib/chisel.ts for all data calls. Do not call fetch
directly. The helper handles base URL and auth.

Env vars in the host project

.env.local — gitignored
NEXT_PUBLIC_CHISEL_URL=https://api.example.com/v1/myapp
NEXT_PUBLIC_CHISEL_KEY=ck_live_pub_…

Auth flows in generated UIs

v0 will happily generate a sign-in form, but you should be explicit about token storage. A safe default:

  • Access token in memory only (a React context, or an in-app store). Don't put it in localStorage.
  • Refresh token in an httpOnly cookie set by a server action. Off-limits to client JS.
  • API key in NEXT_PUBLIC_CHISEL_KEY if it's the publishable kind. Never ship a server-scope key to the browser.

Spell this out in the prompt so v0 doesn't default to "throw the token in localStorage."

Refreshing context when the schema changes

v0 doesn't pull live context — so when the schema moves, you need to repaste the new llms.txt. Two patterns help keep this manageable:

  • Branch chats by feature. Don't reuse the same v0 chat for unrelated features. Each chat starts with the current brief; you'll naturally repaste at feature boundaries.
  • Keep a copy in the repo. Save llms.txt at docs/chisel.llms.txt in the host project. When you regenerate, replace that file. Teammates can grab the latest version from one canonical spot.

Where v0 stops

v0 generates UI code well; it doesn't operate on your backend. The chisel.to platform-side changes (creating tables, defining custom endpoints, configuring auth providers) happen in the dashboard or through a tool with MCP support — Cursor, Claude Code or Windsurf.

A natural workflow: change the schema in Cursor (or the dashboard), regenerate llms.txt, repaste it into a fresh v0 chat to generate the UI for the new shape.

Security & key hygiene

  • Only put the publishable key in any code v0 generates. Never paste a server-scope key into a v0 chat — assume anything in a prompt could leak.
  • Generated code references env vars; the actual key lives in your host project's .env.local.
  • v0 itself doesn't send requests to your backend. Whatever it generates only runs when you copy it into your project.

Troubleshooting

v0 invents endpoints or columns

Either you didn't paste llms.txt at the top, or the brief is too far back in a long chat. Repaste, and add the line "Do not invent endpoints or fields not in this brief" right after the framing.

Generated code fetches the wrong URL

Check that the brief lists your project's actual base URL (it's the first content line). If you have multiple projects, you might have grabbed the wrong file.

Generated code uses localStorage for tokens

You didn't tell it not to. Add a token policy to your prompt: "Store the access token in memory only, never in localStorage. Use an httpOnly cookie set by a server action for the refresh token."

Auth requests come back 401

The generated code probably uses the wrong header format. The auth conventions are explicit in the brief — "use the auth conventions exactly as in the brief" in the prompt nudges v0 back on track.

The UI looks great but ignores my data model

v0 sometimes prioritizes design intent over data correctness. Lead the prompt with the data shape first, then the design: "Render each post (resource: posts) with these columns: title, meta_description, author_id (look up via authors). The design: Tailwind cards in a 3-column grid."

Next steps

  • Once you have a UI, jump into Cursor or Windsurf to iterate on it with backend access.
  • If you want a similar prompt-driven flow with pinned context, see the Lovable guide.
  • Open the auto-generated API reference when you need exact field types and validation rules.