Prose diff
Suggested edits shown inside the text, removed words in red and added words in green, to accept one by one or all at once.
Loading preview…
Install
- 1
Run this command in your Tailwind CSS project. It adds
components/ai/prose-diff.tsxand installs its dependencies.$ npx shadcn@latest add https://gradientdeck.com/ai-components/r/prose-diff.json
Usage & API
Example
import { ProseDiff } from "@/components/ai/prose-diff"
<ProseDiff
parts={[]}
states={states}
onResolve={() => {}}
onResolveAll={() => {}}
/>Props ProseDiff
| Prop | Type & description | Required |
|---|---|---|
parts | DiffPart[] | Required |
states | Record<string, ChangeState> | Required |
onResolve | (id: string, state: ChangeState) => void | Required |
onResolveAll | (state: "accepted" | "rejected") => void | Required |
title | string | Optional |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Default is the card: header, counts, Reject all, Accept all, and the paragraph. Changes that are still pending are buttons. Resolved ones become ordinary spans, with a short fade. The hover toolbar is only for pending changes, and its buttons are removed from tab order so the change itself is the single stop. Click and the check both accept. The X and the Backspace or Delete keys reject. The count line is aria-live. The preview starts with the spelling change flipping to accepted, and the rest left pending, including a deletion with an empty insert and an insertion with an empty delete, so both shapes are on screen. Show the suggestions again appears only in the preview after the map has no pending ids. The component has no reset button of its own.
Choosing a layout
Use it when a person should see exactly which words moved before they commit, and should be allowed to keep some of the rewrite. A tone pass, a style-guide pass, or a grammar pass usually has a few local edits and a lot of unchanged prose. Painting only the edits, in place, is easier to trust than a second column. Accept all is there when the pass looks right. Reject all is there when it does not. The per-change buttons are there for the mixed case. Ghost completion and Inline prompt and Selection toolbar are how an edit gets requested. This is how a list of edits gets reviewed. Those three do not render deletions.
Do not use it for two whole versions of a long document side by side. Version history has a compare variant with two panes, a change navigator, and a save step. Prose diff is one paragraph, or a run of text you assemble, with its own header. Do not use it as a code diff. There is no line gutter, no monospaced mode, and no hunk header. Parts are inline in a wrapping paragraph. If you need the original and the rewrite both fully readable at once, rather than interleaved, this marking will feel cramped once most of the sentence is red and green. Split the view instead of stacking more chips into the sentence.
How do I reject a single change?
Focus the change and press Backspace or Delete, or press the X that appears on hover and focus. Both call onResolve with rejected. Clicking the words, or the check, accepts. Reject all and Accept all only touch changes that are still pending. Already resolved ids stay as they are.
What remains in the sentence after a decision?
Accepted text is the insert string. Rejected text is the delete string. If that string is empty, nothing is left for that change, which is how a pure insertion disappears on reject and a pure deletion disappears on accept. Same-parts are untouched. The colour and the buttons go away either way.