affectUI › Components › Agents

Judge Bracket

How an LLM judge picks the best of several attempts, as a tournament – two answers at a time, the one it prefers moves on, the other fades back; the winner's way through the bracket lights up, and every match opens both answers side by side with the scores and why.

Part of affectUI Pro. Install: npx shadcn@latest add https://www.affectui.com/r/judge-bracket.json · Import from @/components/ui/judge-bracket

When to use

  • LLM-as-a-judge: several models, prompts or temperatures answer the same task and a judge picks the best, two at a time
  • Showing why a generated answer won – every match with both answers side by side, the scores and the judge's reason
  • Best-of-n for coding agents: several fixes for the same bug, judged on whether they solve the cause, pass the tests and stay small

When not to use

  • Several attempts running at once, without a judge comparing them – use Parallel Attempts
  • Trying models one after another until one works – use Fallback Chain
  • Comparing many cells of work at a glance – use Fan-out Grid

Usage

import { JudgeBracket, type Contender } from "@/components/ui/judge-bracket";

const contenders: Contender[] = [
  { id: "a", name: "Sonnet · warm", meta: "142 words", output: "Subject: Your 14 days start now…" },
  { id: "b", name: "GPT-5 · formal", meta: "168 words", output: "Subject: Welcome to your trial…" },
  { id: "c", name: "Gemini · bullets", meta: "104 words", output: "Subject: 3 steps to your first project…" },
  { id: "d", name: "Sonnet · short", meta: "96 words", output: "Subject: Start your first project…" },
];

export function BestEmail() {
  return (
    <JudgeBracket
      contenders={contenders}
      verdicts={{ "0:0": { winner: "a", scores: [8.1, 7.4], reason: "Warmer, and it names the trial length." } }}
      judging="0:1"
      judge="Claude Opus"
      task="Welcome email for new trial users"
      onPick={(winner) => send(winner.output)}
    />
  );
}

Props

NameTypeDefaultDescription
contendersContender[]Every attempt, in seed order – 2, 4, 8 or 16; neighbours meet in the first round
Contender{ id, name, icon?, meta?, output? }name – what makes this attempt different; icon – e.g. the model maker's logo; meta – a quiet line like "118 words · 2.1s"; output – what it produced
verdictsRecord<string, { winner, reason?, scores? }>What the judge decided, by match "round:match" from 0 – e.g. "0:2" is the third match of the first round; scores are for the upper and the lower contender
judgingstringThe match the judge works on right now, same key – both names shimmer
judgestringWho judges – e.g. "Claude Opus"
taskstringWhat the attempts are for – shown muted next to the title
onPick(winner: Contender) => voidUse the winner – without it, the final has no button
titlestring"Judge"Heading of the bar
classNamestringExtra class on the root

Keyboard

  • Tab: Into the bracket, match by match, then to the answers and Use
  • ← / →: To the match this one feeds, and back to the first one feeding it
  • ↑ / ↓: The match above and below in the same round
  • Enter / Space: Shows that match's answers; again goes back
  • Esc: Back to what the judge works on

Accessibility

  • Every match is a button that says its round, both contenders and who won – or that it's being judged
  • The match below the bracket is announced politely when it changes, and the bar says who's ahead
  • Winning is never color alone: the winner keeps its full color and a check or its score, the other fades and its line is dashed

Responsive

  • Every slot has a fixed height, so nothing moves while the judge works; the rounds share the width
  • When the rounds don't fit side by side it becomes a list per round, two contenders a row, and the answers stack
  • Dark mode built in: switches when an ancestor (usually <html>) has the class dark or data-theme="dark"

Requirements

  • React 19

Related

  • Parallel Attempts – An agent tries the same task several ways at once and the best one wins – every attempt races as a row with its model, approach and live bar, then gets a score; the best is marked and picked, and you can switch to another
  • Fallback Chain – One request finding a model that answers, as a small flow diagram – the request branches out to each model in its fallback order, a light runs to the one being tried, failed branches break red, and the one that answers runs on to the response
  • Fan-out Grid – One agent hands a big job to many workers – every task a cell on a live grid that fills in as they get through it, with retries, failures, cost and the time left, and the details of any task on hover
  • Model Picker – The model menu of an AI chat – point at a model and its speed, quality and cost bars fill, switch on extended thinking and its effort unfolds, while the tray rolls the first-token time and price