Integrating Stable Diffusion API into a Next.js app for automated image generation
The biggest mistake most people make is calling the API directly from the client. You'll leak your API key and hit CORS issues. I wrapped the logic in a Next.js Route Handler. One critical config tip: set your maxDuration in route.ts if you're on Vercel, as image generation can sometimes hang for 10-20 seconds, which might trigger a serverless timeout on the hobby plan.
Here is the core implementation of the API route using the fetch API:
// app/api/generate/route.ts
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const { prompt } = await req.json();
const response = await fetch(
'https://api.stability.ai/v1/generation/stable-diffusion-xl-1024-v1-0/text-to-image',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${process.env.STABILITY_API_KEY}`,
},
body: JSON.stringify({
text_prompts: [{ text: prompt, weight: 1 }],
cfg_scale: 7,
height: 1024,
width: 1024,
steps: 30,
samples: 1,
}),
}
);
if (!response.ok) throw new Error('Stability API failed');
const result = await response.json();
const base64Image = result.artifacts[0].base64;
return NextResponse.json({ image: `data:image/png;base64,${base64Image}` });
}To make this production-ready, don't just dump the base64 string into an <img> tag. It bloats the DOM and kills performance. I used Claude Code to refactor my upload flow so that the server-side route immediately pipes the base64 data to an S3 bucket (or Uploadthing) and returns a permanent URL.
Regarding productivity, I've found that "Prompt Engineering" the API is where most developers fail. If you send a raw user input, the results are hit-or-miss. I implemented a "Prompt Enhancer" step using a lightweight LLM call before hitting Stable Diffusion.
My prompt enhancement pipeline:
- User Input: "a futuristic car"
- LLM Enhancement: "Professional studio photography of a futuristic aerodynamic concept car, cinematic lighting, 8k resolution, hyper-realistic, metallic silver finish, blurred urban background"
- Final Result: Much more consistent quality across the app.
A major "gotcha" I encountered was the image aspect ratio. SDXL is picky about dimensions. If you try to send a custom width/height that isn't a multiple of 64, the API will throw a 400 error. I wrote a small helper function to snap any user-defined dimensions to the nearest 64-pixel increment.
For the frontend, using react-hot-toast for the loading state is a lifesaver. Because the API call takes a few seconds, a simple "Generating..." spinner isn't enough. I added a progress bar that mimics loading to reduce perceived latency, which keeps the user from clicking "Generate" five times while the server is still processing the first request.
All Replies (0)
No replies yet — be the first!
