Recipe
Recipe: v0-like component generator
Build a prompt-to-component pipeline that outputs production-ready Tailwind + React code, styled in Meridian violet and pink.
Overview
This recipe wires an LLM endpoint to a streaming UI that renders generated JSX in a live preview. The pipeline accepts a natural-language prompt, enforces Meridian design tokens, and returns a complete functional component. No shadcn/ui, no Radix — just Tailwind and native HTML.
Ingredients
- Next.js 14 App Router with a server action or API route for LLM calls
- Streaming response parser (ReadableStream + TextDecoder)
- Sandboxed iframe or dynamic import for preview rendering
- System prompt enforcing violet #8B5CF6, pink #F472B6, dark #0A0612
System Prompt
You are a React component generator for Meridian.
Output ONLY valid JSX with Tailwind classes.
Design tokens:
- Background: bg-[#0A0612]
- Primary: #8B5CF6 (violet)
- Accent: #F472B6 (pink)
- Text: text-white / text-gray-400
- Borders: border-[#8B5CF6]/20
- Cards: rounded-lg, subtle border
Do NOT import any UI library. Use native HTML
elements styled with Tailwind. Return a single
default-export functional component.Client Component Shell
'use client';
import { useState } from 'react';
export default function Generator() {
const [prompt, setPrompt] = useState('');
const [code, setCode] = useState('');
async function generate() {
const res = await fetch('/api/generate', {
method: 'POST',
body: JSON.stringify({ prompt }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let result = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value, { stream: true });
setCode(result);
}
}
return (
<div className="space-y-4">
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
className="w-full rounded-lg border border-[#8B5CF6]/20
bg-[#0A0612] p-4 text-white"
rows={4}
placeholder="A pricing card with three tiers..."
/>
<button
onClick={generate}
className="rounded-lg bg-[#8B5CF6] px-6 py-2
font-medium text-white hover:bg-[#7A4DE0]"
>
Generate
</button>
{code && (
<pre className="overflow-x-auto rounded-lg border
border-[#8B5CF6]/20 bg-[#0A0612] p-6
text-sm text-gray-300">
<code>{code}</code>
</pre>
)}
</div>
);
}API Route
// app/api/generate/route.ts
export async function POST(req: Request) {
const { prompt } = await req.json();
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4o',
stream: true,
messages: [
{ role: 'system', content: SYSTEM_PROMPT },
{ role: 'user', content: prompt },
],
}),
});
return new Response(response.body, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}Notes
- • Stream the response so the user sees code appear token-by-token.
- • Validate generated code before rendering — strip markdown fences and imports.
- • For production, add rate limiting and prompt sanitization.
- • The preview iframe should use a separate origin to prevent XSS.