AI Interface Components

Generate-and-Pick Flow

Prompt, skeletons, then a row of generated results slides in for the user to pick one that expands for confirmation — the standard flow for image and video generators.

This is a WebberUI Pro component

The install command below needs a licence to run; without one it returns 401.

How to install Pro components →See the plans →

The most common flow in image and video generators: type → generate → a row of options slides past → pick one. The component models it as a four-phase state machine (idle, generating, results, picked): the input row has an auto-growing multi-line prompt plus ratio and count chips; while generating, skeleton cards sized to the ratio show a diagonal shimmer; when results arrive they replace the skeletons one by one, sliding in from the right, and the row scrolls horizontally; click any card and it expands into the preview above with a shared layout animation, with "Use this one", "Regenerate" and "Vary" below. Generation itself is entirely up to your onGenerate.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add "https://webberui.com/r/generate-and-pick-flow.en.json?t=<install token>"

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

4
80
Active edited usageDefault props
<GenerateAndPickFlow/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add "https://webberui.com/r/generate-and-pick-flow.en.json?t=<install token>"

Or, once registries are configured in components.json, install it as @webberui/generate-and-pick-flow.

Usage

import {
  GenerateAndPickFlow,
  type GenerateAndPickFlowResult,
} from "@/components/ui/generate-and-pick-flow";

async function generate(
  prompt: string,
  options: { ratio: string; count: number },
): Promise<GenerateAndPickFlowResult[]> {
  const res = await fetch("/api/generate", {
    method: "POST",
    body: JSON.stringify({ prompt, ...options }),
  });
  const images: { id: string; url: string }[] = await res.json();
  return images.map((img, i) => ({
    id: img.id,
    title: `版本 ${i + 1}`,
    preview: <img src={img.url} alt="" className="object-cover" />,
  }));
}

<GenerateAndPickFlow
  defaultPrompt="黃昏海邊的紙飛機,水彩風格"
  onGenerate={generate}
  onPick={(result) => console.log("採用", result.id)}
/>;

Props

PropTypeDefaultDescription
onGenerate(prompt: string, options: GenerateAndPickFlowOptions) => Promise<GenerateAndPickFlowResult[]>—Generator; on reject, error cards and a retry button are shown
onPick(result: GenerateAndPickFlowResult) => void—Called when "Use this one" is pressed in the enlarged preview
defaultPromptstring""Initial prompt text
placeholderstring"描述你想生成的畫面…" (Describe the image you want…)Prompt placeholder
ratiosstring[]["1:1", "16:9", "9:16"]Ratio chips; the first is the default
countnumber—Controlled result count (2–6)
defaultCountnumber4Initial count in uncontrolled mode
onCountChange(count: number) => void—Called when the count changes
status"idle" | "generating" | "results" | "picked"—Controlled phase; managed internally when omitted
onStatusChange(status: GenerateAndPickFlowStatus) => void—Called when the phase changes
shimmerbooleantrueWhether skeleton cards show a diagonal shimmer
accentstring—Accent for the selected outline; falls back to --wb-generate-pick-accent
staggernumber—Delay between results sliding in, in ms; reads --wb-generate-pick-stagger when omitted
classNamestring—Passed through to the outermost container

GenerateAndPickFlowResult

FieldTypeDefaultDescription
idstring—Unique id, used for the layout animation and focus restoration
previewReactNode—Preview content; fills the ratio-cropped card
titlestring—Title, shown under the enlarged preview and used as the card's accessible name

GenerateAndPickFlowOptions

FieldTypeDefaultDescription
ratiostring—The selected ratio
countnumber—How many results to generate
variationOfGenerateAndPickFlowResult—The result being varied when "Vary" is pressed; undefined for normal generation

How it works

  • CSS variables: --wb-generate-pick-shimmer (default 1.4s) sets the length of one shimmer pass, --wb-generate-pick-stagger (default 80ms) the interval between results appearing, and --wb-generate-pick-accent (default #8b5cf6) the selected outline color. The accent and stagger props override their variables when set
  • One-by-one reveal: once the promise resolves, skeletons are swapped for results at the stagger interval, each sliding in from x 60px and opacity 0 on a spring (stiffness 300, damping 30)
  • Enlarged preview: the clicked card flies up via a shared layoutId (stiffness 260, damping 28), leaving a same-size "Selected" outline in place while the other cards shrink and dim; Esc goes back
  • Horizontal scrolling: the results row uses scroll-snap and maps the mouse wheel to horizontal scrolling, handing the wheel back to the page at either end
  • No mix-ups: when Generate is pressed repeatedly, only the latest call's results are used; earlier promises that resolve later are discarded
  • Vary: "Vary" calls onGenerate again with the same prompt and passes the varied result in options.variationOf

Accessibility

  • Enter submits the prompt and Shift+Enter inserts a newline; Enter pressed while an IME (e.g. Chinese) is composing does not submit
  • The results row is role="listbox" and each card is role="option" with aria-selected; Left/Right arrows and Home/End move focus, Enter or Space picks
  • While generating the row has aria-busy, and an aria-live region announces "Generating N images" and "Generated N images"; failures show the error and a retry button with role="alert"
  • After picking, focus moves to "Use this one"; Esc returns to the results row and puts focus back on the original card
  • When the user's system has reduce motion enabled, skeletons have no shimmer, results appear immediately, and picking switches directly with no layout flight