git-diff-view: one diff component across React, Vue, Solid, Svelte, Lynx and the terminal
A Diff View component for React / Vue / Solid / Svelte / Ink, same as Github
At a glance
- What is it?
- git-diff-view is a TypeScript monorepo that renders diffs for six front-end targets, from React and Vue to Svelte, Solid, Lynx and an Ink-based terminal viewer. The engines are separated from the UI packages, which makes it flexible to embed and fairly demanding to set up correctly.
- Who is it for?
- git-diff-view suits teams that already ship across several front-end stacks and want one diff renderer rather than a separate library per framework, and that are willing to follow the setup order the file engine expects. It does not suit projects that want a single drop-in package with no theme initialisation, no separate highlighter package and no build from a workspace repository.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Six front-end packages and two engines in one monorepo
git-diff-view exists to solve a packaging problem. A diff viewer is a common requirement in code review tools, teaching platforms and internal developer dashboards, and the usual outcome is a different component for each framework. This repository takes the opposite approach and publishes a component per framework while keeping the diff logic in shared packages. The README lists @git-diff-view/react, @git-diff-view/vue, @git-diff-view/solid, @git-diff-view/svelte, @git-diff-view/lynx and @git-diff-view/cli as the UI packages, with @git-diff-view/core and @git-diff-view/file as the core libraries. The intent, stated in the project description, is a diff view component that looks like GitHub's, usable in React, Vue, Solid, Svelte and Ink. The audience is engineers embedding diffs into their own product, not developers running git in a terminal, since the CLI package here is a viewer rather than a version control tool.
Two ways in: supplied hunks or a computed file comparison
The component accepts data in one of two shapes, and picking the wrong one is the most common source of confusion in the documentation. In git mode, the caller passes an object holding oldFile, newFile and hunks, which means the caller has already produced the diff, typically with a server-side git call, and the component only renders it. In file mode, the caller hands two raw file contents to a helper called generateDiffFile and lets the library compute the difference in the browser or in a worker. These are genuinely different approaches rather than two names for one thing. One treats the library as a renderer and leaves diffing to your backend; the other treats it as the diff engine and keeps the payload down to two strings plus a language identifier.
Installing a framework package and rendering the first diff
Installation is per framework, and the README gives the commands for each target. For React, Vue, Solid, Svelte, Lynx and the CLI the packages are installed with pnpm:
# React
pnpm add @git-diff-view/react
# Vue
pnpm add @git-diff-view/vue
# Solid / Svelte
pnpm add @git-diff-view/solid
pnpm add @git-diff-view/svelte
# ReactLynx (Lynx)
pnpm add @git-diff-view/lynx
# CLI (terminal / Ink)
pnpm add @git-diff-view/cliThe React entry point takes the diff data, a mode and some display options:
import { DiffView, DiffModeEnum } from "@git-diff-view/react";
import "@git-diff-view/react/styles/diff-view.css";
<DiffView
data={{
oldFile: { fileName: "old.ts", content: "..." },
newFile: { fileName: "new.ts", content: "..." },
hunks: ["..."]
}}
diffViewMode={DiffModeEnum.Split}
diffViewTheme="dark"
diffViewHighlight
/>Note the stylesheet import, which is required and easy to miss, and the DiffModeEnum.Split value, which selects the side-by-side layout over the unified one. Switching to unified view is a matter of changing that enum.
File mode needs theme init and line building before it renders
The advanced example shows the file comparison path, and the order of the calls matters more than it looks. The helper comes from a separate package, @git-diff-view/file, and returns an object you then configure before handing to the component:
import { DiffFile, generateDiffFile } from "@git-diff-view/file";
// File comparison mode
const file = generateDiffFile(
"old.ts", oldContent,
"new.ts", newContent,
"typescript", "typescript"
);
file.initTheme('dark');
file.init();
file.buildSplitDiffLines();
<DiffView diffFile={file} />Three separate steps are required before rendering: the theme is initialised, the file is initialised, and the split diff lines are built. Skip any of them and you get a component with no content rather than an error message telling you which step is missing. The two trailing string arguments are the language identifiers for the old and new files, which is also how the syntax highlighter decides what grammar to load. This is the main operational cost of the library: the setup is explicit and manual, and the README does not explain what happens if the same file is initialised twice.
Three interchangeable highlighters behind one HAST pipeline
Syntax highlighting is not baked into the core. It ships as three separate packages that all feed the same pipeline, and the README compares them directly. The lowlight package wraps highlight.js and is described as the default with broad language coverage. The shiki package uses TextMate grammars and is presented as the most accurate option, with VS Code themes. The lezer package uses CodeMirror's Lezer parsers and is described as pure JavaScript with no WASM, which makes it the one to pick for constrained runtimes such as Lynx. Switching between them is meant to be a drop-in decision:
import { getDiffViewHighlighter } from "@git-diff-view/shiki";
// or: import { highlighter } from "@git-diff-view/lezer";
const highlighter = await getDiffViewHighlighter();
diffFile.initSyntax({ registerHighlighter: highlighter });The highlighter is created asynchronously and then registered on the diff file, which means highlighting cannot start until that promise resolves. The cost of the accurate option is package size and startup time; the cost of the WASM-free option is a smaller grammar set. The README does not publish any size or timing figures for these packages, so the trade-off has to be judged by your own bundle analysis.
The terminal package reuses the same component through Ink
The CLI package applies the same diff model to a terminal, built on Ink, and is not a separate implementation. It supports split and unified views, syntax highlighting and a fixed-height viewport that scrolls. The component signature stays recognisable:
import { DiffView, DiffModeEnum } from "@git-diff-view/cli";
<DiffView
data={{ oldFile, newFile, hunks }}
diffViewMode={DiffModeEnum.Split}
diffViewTheme="dark"
diffViewHighlight
width={120}
height={20} // viewport height in visual rows (optional)
onScrollChange={console.log}
/>Height is measured in visual rows rather than character lines, which matters when highlighted output wraps. When height is set, the component exposes scroll ref methods such as scrollToTop, scrollDown and getScrollState. There is also a CodeView component that renders a single file, with the same scroll exposure. The CLI documentation lives in packages/cli/readme.md, with a scroll API section and implementation notes in packages/cli/SCROLL.md, so the terminal behaviour is documented separately from the main README.
A 0.1.x package set with a workspace release pipeline
The project is organised as a pnpm workspace, with [email protected] pinned as the package manager and per-package directories under packages/. The root package.json is private and wires the workflow together with ts-node scripts: dev:packages, build:packages and start:release, plus dev tasks per example app under ui/. The release path is explicit. Running the release script first executes pre:release, which lints with eslint, formats with prettier, cleans dist, dev and .cache directories across the workspace, and builds every package. Only after that does the release script run the release script itself. That ordering is worth knowing before you fork the repository, since a failing lint or format check stops a release before any artifact is produced. The published versions are still in the 0.1.x range: v0.1.8 on 2026-10-15, v0.1.7 on 2026-07-13 and v0.1.5 on 2026-05-26. The last push was on 2026-10-17, so the repository is being worked on, though at 0.1.x you should expect breaking changes in minor bumps. The licence is MIT.
Editorial conclusion
git-diff-view suits teams that already ship across several front-end stacks and want one diff renderer rather than a separate library per framework, and that are willing to follow the setup order the file engine expects. It does not suit projects that want a single drop-in package with no theme initialisation, no separate highlighter package and no build from a workspace repository. Before adopting it, check that your diff source can supply hunks in the shape the component expects, confirm that the highlighted language identifiers match the ones your highlighter package supports, and verify that the version of @git-diff-view/core or @git-diff-view/file you pin matches the framework package you install.
Frequently asked questions
How do I install git-diff-view for React?
Run pnpm add @git-diff-view/react, then import the component from @git-diff-view/react and the stylesheet from @git-diff-view/react/styles/diff-view.css. Each framework has its own package, such as @git-diff-view/vue and @git-diff-view/svelte.
What is the difference between git mode and file mode in git-diff-view?
In git mode you pass oldFile, newFile and hunks, so the diff is computed elsewhere and the component renders it. In file mode you call generateDiffFile with two file contents and language identifiers, and the library computes the difference, after which you call initTheme, init and buildSplitDiffLines before rendering.
Which syntax highlighter should I use with git-diff-view?
The lowlight package uses highlight.js and covers many languages, the shiki package uses TextMate grammars for VS Code accuracy, and the lezer package uses pure JavaScript Lezer parsers with no WASM for constrained runtimes. You register the chosen highlighter with initSyntax after creating it asynchronously.
Can git-diff-view render diffs in a terminal?
Yes, through the @git-diff-view/cli package, which is built on Ink and supports split and unified views, syntax highlighting and a fixed-height scrolling viewport measured in visual rows. The same package also exports a CodeView component for a single file.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mrwangjusttodo-git-diff-view)Was this useful?