klyv-ui

Diff Summary

Forms

About

A per-field list of pending changes to a form, showing the old and new value side by side with set/removed callouts and a per-field revert action, used as a review step before submitting edits.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Diff Summary to your project and fits it to your theme.

Code

How Diff Summary is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import DiffSummary from "@/components/ui/diff-summary";
import type { FieldChange } from "@/components/ui/diff-summary";
import { Surface } from "@/components/ui/diff-summary-utils/surface";
import { Text } from "@/components/ui/diff-summary-utils/text";
import { useState } from "react";

const CHANGES: FieldChange[] = [
  {
    field: "limit",
    label: "Daily limit",
    from: "$500",
    to: "$5,000",
    note: "A ten-fold rise. Two approvals are needed.",
  },
  {
    field: "reference",
    label: "Reference",
    from: "September rent",
    to: "October rent",
  },
  {
    field: "notify",
    label: "Notify by",
    from: "Email",
    to: "Email and push",
    added: false,
  },
  { field: "nickname", label: "Nickname", from: "Rent account", removed: true },
  { field: "category", label: "Category", to: "Housing", added: true },
];

export default function DiffSummaryDemo() {
  const [changes, setChanges] = useState(CHANGES);

  return (
    <div className="flex w-full max-w-xl flex-col gap-3 bg-background p-6 text-foreground">
      <Surface variant="card" padding="lg" className="items-start gap-3">
        <Text size="heading">Review before sending</Text>
        <DiffSummary
          changes={changes}
          label="Changes to review"
          onRevert={(field) =>
            setChanges((current) =>
              current.filter((change) => change.field !== field),
            )
          }
        />
        <div className="flex gap-2">
          <button
            type="button"
            disabled={changes.length === 0}
            className="inline-flex h-8 shrink-0 items-center justify-center gap-2 rounded-full bg-accent px-3.5 text-[12px] font-semibold leading-none text-accent-ink transition-colors hover:bg-accent-strong disabled:pointer-events-none disabled:opacity-40"
          >
            Submit for approval
          </button>
          <button
            type="button"
            onClick={() => setChanges(CHANGES)}
            className="inline-flex h-8 shrink-0 items-center justify-center gap-2 rounded-full px-3.5 text-[12px] font-semibold leading-none text-ink-soft transition-colors hover:bg-surface-muted hover:text-ink"
          >
            Reset
          </button>
        </div>
      </Surface>
      <Text
        size="caption"
        tone="faint"
        leading="normal"
        className="max-w-[68ch]"
      >
        Reverting is per field. A single Discard forces an all-or-nothing
        decision on someone who has spotted one mistake among five deliberate
        edits — which is why that person retypes everything instead.
      </Text>
    </div>
  );
}

More by klyv-ui

View all
  • Gantt Chart

    Gantt Chart

    56
  • Klyv Animated Beam

    Klyv Animated Beam

    47
  • Pareto Chart

    Pareto Chart

    40
  • Shader Canvas

    Shader Canvas

    35
  • Sunburst Chart

    Sunburst Chart

    23
  • Morphing Text

    Morphing Text

    21
  • Gooey Loader

    Gooey Loader

    20
  • Gravity Tags

    Gravity Tags

    17

More in Forms

View all
  • Premium Auth

    Premium Auth

    500
  • Login 1

    Login 1

    52
  • Authentication Card

    Authentication Card

    47
  • Book A Demo 3

    Book A Demo 3

    83
  • Book A Demo 1

    Book A Demo 1

    69
  • Contact Card

    Contact Card

    1K
  • Login Card

    Login Card

    32
  • Login 2

    Login 2

    7