Fix: v0 × security

v0 made the frontend beautiful.
It left the backend to you.

v0 is the best of the AI builders at what it actually does: React components on shadcn/ui that look designed. The trouble starts when generated components quietly become the whole app. Independent audits of v0 output document API keys embedded in client components, secrets under NEXT_PUBLIC_, and route handlers with no authentication (vibe-eval.com, ship-safe.co). Your components are worth keeping — they just need a backend that treats them as untrusted, like every backend should.

Where v0 code goes wrong: the demo pattern becomes the app

v0 optimizes for a component that works in preview, and the shortest working version of 'fetch data from an API' is a client component calling the API directly with the key inline. Audits of generated v0 apps keep finding the same set: third-party calls made from the client with embedded credentials, secrets stored under NEXT_PUBLIC_ env vars — which Next.js by design compiles into the public browser bundle — XSS via dangerouslySetInnerHTML, and form handlers that validate in the browser but never on the server (vibe-eval.com). ship-safe.co's audit puts it plainly: v0-generated route handlers do their job but rarely include authentication checks, so anyone who guesses the URL is a user. To be fair to v0, there's no headline breach with its name on it the way there is for other builders — but the pattern is the same across AI builders, and the pattern is what gets exploited.

Symptoms to grep for in a v0 codebase

Secrets under NEXT_PUBLIC_

Anything prefixed NEXT_PUBLIC_ is compiled into the browser bundle — that's its documented purpose. Generated code regularly parks real secrets there because it makes the client component work. Grep for the prefix and audit every hit.

Client components calling third-party APIs

A 'use client' file that fetches OpenAI, Resend, or Stripe directly is shipping its credential to every visitor. Those calls belong behind your own server route.

Route handlers with no session check

Generated app/api handlers typically parse the request and do the work — with no check on who's asking. Every handler needs auth before logic, not as a decoration on the page that links to it.

Server actions that trust the form

A server action receiving form data is a public endpoint. If it doesn't validate its input server-side, anyone can invoke it with any payload — the browser-side validation never ran for them.

dangerouslySetInnerHTML on user content

The API is named as a warning. If v0 used it to render anything user-influenced, you have an XSS surface — one script tag in a profile field away from session theft.

The wall: one contract, enforced on both sides

On useDeploy the client can't invent its own idea of a payload. Zod schemas live in a shared contracts package: the server validates every request with them, and the client's types are generated from the server's OpenAPI output. When the two drift, CI fails the build before anything merges — the exact class of gap that lets browser-validated-only forms through disappears.

CI — the contract freshness gate
 1  $ git push origin feat/profile-form
 2  ci: typecheck ................ ✓
 3  ci: api-types-fresh .......... ✗
 4      apps/server/openapi.json is stale
 5      client payload has field the server schema doesn't know
 6      run `bun run generate:api` and commit the diff
 7  ci: merge blocked
 8  
 9  # validation isn't a client-side courtesy. it's the same
10  # Zod schema, enforced where attackers can't skip it.

Standing on the base, this is already done

Server-side validation everywhere

Every endpoint validates its input with Zod schemas from the shared contracts package. There is no route where 'the form checked it' is the only check.

Auth checked where it counts

Sessions are server-side rows in Postgres, revocable, carried by cookies — and protected routes verify them in middleware before any handler runs. No handler ships open by default.

RBAC and API keys per organization

Permissions are organization-scoped with role-based access control, and programmatic access goes through real API keys — not a guessable URL being the only barrier.

Secrets stay on the server

Credentials live in a Zod-validated env schema loaded at boot, server-side only. The NEXT_PUBLIC_ foot-gun has nothing to load.

Signed and throttled at the edges

Outgoing webhooks are HMAC-signed; incoming traffic is rate-limited by tier, with auth endpoints bucketed by IP and email.

450+ tests and an E2E gate

The behaviors above aren't aspirations — they're asserted by 450+ automated tests and a Playwright E2E suite gating CI on every merge.

Migration path: your shadcn components drop straight in

This is the easiest migration of any AI builder, because useDeploy's client is Next.js with shadcn/ui and Tailwind — the exact vocabulary v0 generates. Your components paste into the design system and pick up the token theme. The real work is rewiring data flow: client-side fetches to third parties become calls through the typed openapi-fetch client to your own endpoints, where the key lives server-side and the payload is Zod-validated. Server actions map to use cases behind Express controllers — same ergonomics, but with auth middleware and contract validation in front instead of convention. Move screen by screen: port the component, point it at a typed endpoint, delete the embedded fetch. Most teams find the components were never the problem — the missing floor under them was.

v0 × security, asked directly

Can I paste v0 components into the base as-is?+
Close to it. The base uses shadcn/ui and Tailwind with a token-driven theme, so v0 output slots in with minor class adjustments. What you replace is any data fetching inside the component — that goes through the typed API client instead.
Is v0 itself insecure?+
No — and it's worth being precise. v0 generates frontend code, and the documented gaps show up in what generated apps leave unbuilt: server-side validation, authenticated handlers, secret management. The pattern is the same across AI builders. The fix is a backend that assumes the client is hostile.
What happens to my server actions?+
They become use cases: the same logic, moved behind controllers with auth middleware, rate limiting, and Zod contract validation in front. You lose nothing except the ability to accidentally expose one.
Can I keep deploying the frontend on Vercel?+
Yes. The Next.js client deploys wherever Next.js deploys, Vercel included; the Express API runs alongside it (Railway and Docker paths are documented). The security boundary moves to the API, which is the point.

One license. Two ways to own it.

CORE

Founder

$99one-time

A frozen snapshot of UseDeploy. Yours forever, no updates.

  • Frozen-version zip download
  • Full source, commercial use, unlimited projects
  • All 800+ tests · all docs pages
  • 14-day refund
RECOMMENDED

LATEST + UPDATES

Lifetime

$199$249one-time

Always the latest UseDeploy. Re-download every release, free.

  • Latest-version zip — re-downloadable forever
  • Every future release at no extra charge
  • Priority Discord support
  • All 800+ tests · all docs pages
  • 14-day refund

Keep the frontend. Fix the floor.

Your v0 components are good. Give them a backend with validated contracts, real sessions, and org-scoped permissions — built and tested before you arrived.