v0 produces clean Next.js UI quickly, but it does not reason about where a secret is allowed to live. The two risks that matter are a key hardcoded into generated code, and a secret that reaches the browser because it is used in a client component or prefixed with NEXT_PUBLIC. Both are common, and both are easy to miss because the app looks and works fine.
The risks that actually matter
Secrets shipped to the browser
Any value referenced in a client component, or any variable prefixed with NEXT_PUBLIC, is bundled into the client JavaScript and readable by every visitor. A secret used this way is public no matter where you stored it.
Hardcoded API keys
When an integration needs a key, v0 often inlines it so the component renders, and that value ships to the repo and the bundle.
Client-side data access without checks
Generated components that fetch data directly can skip the authorization that belongs on the server.
How to secure a v0 app
- Use secret keys only in server components, route handlers, or server actions.
- Never prefix a secret with NEXT_PUBLIC; that exposes it to the browser.
- Move hardcoded keys into a gitignored .env.local and read them server-side.
- Confirm data-fetching enforces auth on the server, not just in the UI.