Skip to content
KONIGI Patterns

Regenerate

People need another attempt at an answer without retyping their question.

Updated September 12, 2026

Problem

An answer is unhelpful, but the person cannot identify a specific correction. They need a way to request another attempt without retyping the question.

Solution

Offer another response to the same request while keeping the earlier answer available. Use clear version controls so people can compare attempts and choose which one to continue from.

Explain that another attempt may differ without being better. Where useful, let people add direction, such as asking for a shorter answer, or choose a different model.

Make any additional usage cost clear. Preserve the selected response and its follow-up conversation when people return later.

Use when

Another attempt can produce a useful alternative, and people can judge which result meets their needs.

Don’t use when

The task has a right answer the model either reached or didn’t. Rerolling an arithmetic error or a factual lookup is superstition, and offering the control there teaches a bad habit. Also avoid it where a response has already caused a side effect, since the second answer can’t undo the first one’s consequences.

Trade-offs

Repeated attempts consume time and usage without fixing an unclear question. Keeping alternatives prevents loss but complicates the conversation history. Version controls must be visible enough for people to find earlier answers.

Checklist

  • Does the previous response survive, and can the viewer get back to it?
  • Is the count of alternates visible without hovering?
  • Can the viewer give the retry a direction rather than rerolling blind?
  • What happens to turns that came after the one being regenerated?
  • Does the model know it is being asked again, or does it see an identical fresh request?
  • Is a retry recorded as a quality signal anywhere?
  • Does each regeneration bill, and does the viewer know that?
  • Is the control offered on tasks where rerolling cannot help?
  • Can the viewer compare two alternates side by side, or only one at a time?
  • When the conversation is shared or exported, which alternate goes with it?

Compare

ChatGPT keeps earlier attempts on the turn behind a small pager, so the transcript stays linear while the branch is preserved underneath it. Gemini has historically gone the other way and surfaced multiple drafts as a first-class thing to look at rather than something to page through, which treats variation as information instead of an accident. Claude attaches retry to the response and lets the model be switched at the same time, so the second attempt is a different question rather than the same one asked twice. Perplexity frames the rerun around the source set rather than the prose, so trying again is closer to searching again than to rewriting.

Stop generation interrupts an answer in progress. Response actions holds the retry control. Edit and resend changes the question itself.

Model picker can change the model for another attempt. Usage meter shows the cost of retries.

Regenerate anatomy One assistant turn holding three alternate responses behind a pager, with a directed retry offering a different model or a shorter answer, and a branch marker showing that continuing from a different alternate produces a different conversation. Three answers to one question, in one turn ‹ 2 of 3 › try shorter other model from answer 1 from answer 2 the thread the viewer is on is whichever alternate is selected 1 2 3 4 1 THE PAGER Earlier attempts survive and the transcript stays linear. Replacing destroys the compare. 2 A DIRECTED RETRY Shorter, or another model. A bare reroll gives the model nothing it did not have. 3 THE BRANCH POINT Keeping alternates makes the conversation a tree. Most products hide it and should. 4 EACH ONE IS BILLED Every retry regenerates the whole response. Three attempts cost three answers. Rerolling is a dice throw. Editing the question is the correction that addresses the cause.
Wireframe — the pattern's anatomy, not any one product's version of it

Implementation

One turn holding three alternates behind a pager, with a directed retry that asks for a different model or a shorter answer. Continuing from a different alternate produces a different conversation, and the thread the reader is on is whichever one is selected.

shadcn
npx shadcn@latest add button
Tokens
--card--card-foreground--background--accent--accent-foreground--muted-foreground--border--input--chart-1

Five-year term from 1 March 2024 at £42,000 a year, with an upward-only review at the start of year three. The tenant can break at the end of year two on six months' notice, provided the rent is paid up to date and the premises are handed back empty. The deposit is three months' rent, returned within 30 days of the end of the term.

2 of 3

the thread below continues from answer 2

Regenerate.tsxOne turn, every answer it has produced, a pager between them, and two retries that carry a direction.

import { useEffect, useRef, useState } from "react";
import { Button } from "@/components/ui/button";

/** A retry with a direction. A bare reroll gives the model nothing it did not
 *  already have; "shorter" or "another model" turns the dice roll into a
 *  steer. */
export type Retry = { shorter?: boolean; otherModel?: boolean };

type Props = {
  /** Every answer this turn has produced, oldest first. Kept, never replaced:
   *  the viewer retried because the first one was partly right. */
  alternates: string[];
  onRetry: (direction: Retry) => void;
  /** Which alternate the conversation continues from. Once a turn holds
   *  several answers the transcript is a tree, and the selection is the
   *  branch. */
  onSelect?: (index: number) => void;
  /** Which alternate shows first. Defaults to the newest. */
  defaultIndex?: number;
};

export function Regenerate({ alternates, onRetry, onSelect, defaultIndex }: Props) {
  const n = alternates.length;
  const [i, setI] = useState(defaultIndex ?? n - 1);
  const go = (next: number) => { setI(next); onSelect?.(next); };

  // A retry lands on the new answer. The viewer asked for it; it is what they
  // want to see, and the earlier ones are one keypress back.
  const seen = useRef(n);
  useEffect(() => {
    if (n > seen.current) go(n - 1);
    seen.current = n;
  }, [n]);

  return (
    <div>
      <p className="text-sm leading-relaxed text-card-foreground">{alternates[i]}</p>

      <div className="mt-3 flex flex-wrap items-center gap-2">
        {n > 1 && (
          <div className="flex items-center gap-1 text-xs">
            <Button variant="ghost" size="sm" className="h-7 w-7 p-0" onClick={() => go(i - 1)} disabled={i === 0} aria-label="previous answer">‹</Button>
            <span className="tabular-nums text-chart-1" aria-live="polite">{i + 1} of {n}</span>
            <Button variant="ghost" size="sm" className="h-7 w-7 p-0" onClick={() => go(i + 1)} disabled={i === n - 1} aria-label="next answer">›</Button>
          </div>
        )}
        <Button variant="outline" size="sm" className="h-7 rounded-full text-xs" onClick={() => onRetry({ shorter: true })}>try shorter</Button>
        <Button variant="outline" size="sm" className="h-7 rounded-full text-xs" onClick={() => onRetry({ otherModel: true })}>other model</Button>
      </div>

      {/* The tree is kept and hidden; the selection defines the visible
          thread. This line is what makes the branch point findable. */}
      {n > 1 && (
        <p className="mt-2 text-xs text-muted-foreground">the thread below continues from answer {i + 1}</p>
      )}
    </div>
  );
}

demo.tsxHow it is called: three answers, opened on the second. A retry appends a fourth in the direction asked for.

import { useState } from "react";
import { Regenerate, type Retry } from "./Regenerate";

const ANSWERS = [
  "The lease is for five years from 1 March 2024. Rent is £42,000 a year and is reviewed at the third anniversary, upward only, to the open market figure or the passing rent, whichever is higher. The tenant may end the lease at the end of the second year by giving six months' written notice, on condition that all rent has been paid and the premises are returned with vacant possession. A deposit of three months' rent is held in a separate account and returned within thirty days of the end of the term, less any agreed deductions for dilapidations.",
  "Five-year term from 1 March 2024 at £42,000 a year, with an upward-only review at the start of year three. The tenant can break at the end of year two on six months' notice, provided the rent is paid up to date and the premises are handed back empty. The deposit is three months' rent, returned within 30 days of the end of the term.",
  "Five years from March 2024, £42,000 a year, upward-only review at year three. Tenant break at year two on six months' notice if rent is paid and the premises are empty. Deposit of three months' rent, back within 30 days.",
];

/** Three answers to one question. Retry appends a fourth in the asked-for direction. */
export default function Demo() {
  const [alternates, setAlternates] = useState(ANSWERS);
  const retry = (d: Retry) => {
    const next = d.shorter
      ? "Five years, £42,000 a year, reviewed upward at year three. Break at year two on six months' notice. Three months' deposit."
      : "The lease runs 1 March 2024 to 28 February 2029. Annual rent £42,000, upward-only review on 1 March 2027. A tenant-only break on 28 February 2026 needs six months' notice and rent paid up to date. Deposit is three months' rent, refunded within 30 days.";
    setAlternates([...alternates, next]);
  };
  return (
    <div className="rounded-lg border bg-card p-4">
      <Regenerate alternates={alternates} defaultIndex={1} onRetry={retry} />
    </div>
  );
}
What it renders. Identical markup in both panes, with only the token values changing.

Examples

No screenshots yet for this pattern. Browse Products to see other examples.