Build image-generation progress and regenerate controls
Connect an image-generation component to job progress, cancellation and retry without confusing a visual animation with a real job result.
A mountain valley in warm evening light
Generating sample artwork...
Install the components
Add these to a React project with Tailwind CSS configured.
npx shadcn@latest add https://gradientdeck.com/ai-components/r/image-generation-blur.jsonComponents used: Image generation.
1. Represent the job explicitly
Keep the job's progress, completion and cancellation state in the parent. ImageGenerationBlur takes progress between zero and one and a generating or done status. Its current artwork is a seeded SVG landscape, which makes the sample deterministic and independent of an image service.
2. Drive progress from events
The demo advances progress using a clock. In a real application, replace that clock with job events or polling. If your service only reports queued, running and complete, show those stages instead of inventing a percentage. Completion should follow a successful result event.
3. Cancel and regenerate as separate actions
The example's cancel button stops the local timer and removes the unfinished image. Try again and Regenerate start a fresh run. A production cancellation also needs to reach your backend, and stale events from an older job must not overwrite the newest result.
Complete example
Save this as a client component after installing the components above.
"use client";
import { useEffect, useState } from "react";
import { ImageGenerationBlur } from "@/components/ai/image-generation-blur";
export default function ImageGenerationExample() {
const [run, setRun] = useState(0);
const [progress, setProgress] = useState(0);
const [cancelled, setCancelled] = useState(false);
useEffect(() => {
if (cancelled) return;
// A sample job clock. Replace this with progress events from your backend.
const start = performance.now();
const timer = setInterval(() => {
const next = Math.min(1, (performance.now() - start) / 3500);
setProgress(next);
if (next === 1) clearInterval(timer);
}, 80);
return () => clearInterval(timer);
}, [run, cancelled]);
function regenerate() { setProgress(0); setCancelled(false); setRun(value => value + 1); }
return <div className="w-full max-w-xl space-y-3">
{!cancelled && <ImageGenerationBlur prompt="A mountain valley in warm evening light" progress={progress} status={progress === 1 ? "done" : "generating"} seed={run + 3} onRegenerate={regenerate} />}
<p role="status" className="text-xs text-zinc-500">{cancelled ? "Sample job cancelled." : progress === 1 ? "Sample image ready." : "Generating sample artwork..."}</p>
{cancelled ? <button type="button" onClick={regenerate} className="text-sm underline">Try again</button> : progress < 1 && <button type="button" onClick={() => setCancelled(true)} className="text-sm underline">Cancel generation</button>}
</div>;
}Connect your backend
To show generated images, replace the sample Artwork renderer in the copied component with your result image and keep the progress treatment around it. Use a stable job ID, handle failed jobs, and validate image destinations. A stopped browser animation does not prove the server stopped generating or charging for the request.
All guides →