Componenteditors0.96 KB gzipZero dependencies
Prompt Diff Compare
Side-by-side prompt diff with highlights.
Preview
Live Preview
Prompt diff
-1 / +5
Before
Design a blue ticket stub for the evening show
After
Design a cream ticket stub with red stamp for the evening gala show
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add prompt-diff-compareOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { PromptDiffCompare } from "@/components/ui/prompt-diff-compare";
<PromptDiffCompare />Usage guidance
Use Prompt Diff Compare when a ticket stub surface needs Side-by-side prompt diff with highlights. Keep props minimal and prefer composition with sibling stubs.
Avoid Prompt Diff Compare for long form flows or dense data grids where a dedicated layout block fits better.
Source code
prompt-diff-compare.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface PromptDiffCompareProps extends React.HTMLAttributes<HTMLDivElement> {
before: string;
after: string;
beforeLabel?: string;
afterLabel?: string;
}
function words(text: string): string[] {
return text.split(/\s+/).filter(Boolean);
}
/** Side-by-side prompt diff with added/removed word highlights. */
export function PromptDiffCompare({
before,
after,
beforeLabel = "Before",
afterLabel = "After",
className,
...props
}: PromptDiffCompareProps) {
const beforeWords = React.useMemo(() => words(before), [before]);
const afterWords = React.useMemo(() => words(after), [after]);
const afterSet = React.useMemo(() => new Set(afterWords), [afterWords]);
const beforeSet = React.useMemo(() => new Set(beforeWords), [beforeWords]);
const removed = beforeWords.filter((w) => !afterSet.has(w)).length;
const added = afterWords.filter((w) => !beforeSet.has(w)).length;
const pane = (label: string, list: string[], base: Set<string>, tone: "removed" | "added") => (
<div className="flex-1 rounded border border-border bg-background p-2.5">
<p className="font-mono text-[10px] font-bold uppercase tracking-[0.2em] text-muted-foreground">{label}</p>
<p className="mt-1.5 text-xs leading-6 text-foreground">
{list.map((w, i) => {
const changed = !base.has(w);
return (
<span
key={i}
className={cn(
"rounded px-0.5",
changed && tone === "removed" && "bg-accent/20 text-accent line-through",
changed && tone === "added" && "bg-primary/20 font-bold",
)}
>
{w}{" "}
</span>
);
})}
</p>
</div>
);
return (
<div className={cn("w-full rounded-lg border-2 border-foreground bg-card p-3", className)} {...props}>
<div className="mb-2 flex items-center justify-between">
<p className="font-mono text-[10px] font-bold uppercase tracking-[0.2em] text-muted-foreground">
Prompt diff
</p>
<p className="font-mono text-[10px] uppercase tracking-wider text-muted-foreground">
<span className="text-accent">-{removed}</span> / <span className="font-bold">+{added}</span>
</p>
</div>
<div className="flex flex-col gap-2 sm:flex-row">
{pane(beforeLabel, beforeWords, afterSet, "removed")}
{pane(afterLabel, afterWords, beforeSet, "added")}
</div>
</div>
);
}