mistralys/text-diff
A PHP library for comparing text and rendering differences. It computes diffs between strings or files and presents changes in HTML or plain text via a flexible API, making it easy to add clear diff views to PHP applications.
Install it as a normal Composer dependency:
composer require mistralys/text-diff
Start by reading the package README and vendor/mistralys/text-diff/src/ to confirm the current entry-point and renderer names. This is a small, framework-agnostic utility package, so there is no Laravel service provider, config file, migration, or published assets to expect.
The first useful Laravel use case is an admin/audit “what changed?” view: compare two saved versions of a model field, such as body, description, terms, or a CMS page, and render the result as highlighted HTML.
Keep the package behind a small application service so the rest of Laravel does not depend directly on its API:
namespace App\Services;
use Mistralys\TextDiff\TextDiff;
final class TextDiffService
{
public function toHtml(?string $before, ?string $after): string
{
$before = $this->normalize($before ?? '');
$after = $this->normalize($after ?? '');
if ($before === $after) {
return '';
}
// Keep package-specific calls isolated here.
return TextDiff::create($before, $after)->renderHTML();
}
private function normalize(string $text): string
{
return str_replace(["\r\n", "\r"], "\n", $text);
}
}
Then use it from a controller or view model:
public function show(Revision $revision, TextDiffService $diff)
{
return view('revisions.show', [
'diffHtml' => $diff->toHtml($revision->before, $revision->after),
]);
}
<div class="text-diff">
{!! $diffHtml !!}
</div>
Only use unescaped Blade output if you have verified that the package escapes the source text correctly. If user-authored HTML can enter the diff, sanitize the result or render plain text instead.
Store source snapshots, render diffs on demand. Persist the before/after text, not the generated HTML. Render the diff when the audit page is opened so renderer/CSS changes do not require data migrations.
Use it for revision history and audit logs. A common model is a revisions table with revisionable_type, revisionable_id, before, after, user_id, and timestamps. The package handles the presentation; your app handles authorization, retention, and audit metadata.
Add a Blade component for repeated use. Wrap the service in a component so views stay clean:
<x-text-diff :before="$revision->before" :after="$revision->after" />
The component can own the wrapper markup, CSS class names, empty-state message, and “no changes” behavior.
Cache expensive diffs. Large documents can be slow to diff. Cache by revision ID plus a hash/version of the renderer options:
$html = Cache::remember(
"revision-diff:{$revision->id}:v1",
now()->addHour(),
fn () => $diff->toHtml($revision->before, $revision->after)
);
Include the package version or renderer settings in the cache key if output can change after upgrades.
Queue large comparisons. For long legal text, documentation, or imported content, generate the diff in a job and store the HTML in cache/object storage. The request should not block on a very large diff.
Normalize before diffing. Decide explicitly whether whitespace, case, tabs, and line endings matter. Normalize CRLF/CR to LF before passing text to the package. If your product treats whitespace-only changes as noise, strip or collapse it before diffing.
Use plain text or structured output where HTML is not appropriate. HTML highlighting is ideal for admin screens. For APIs, emails, logs, or exports, prefer a plain-text renderer if the package exposes one, or return a small JSON payload with the before/after text and a rendered HTML field only for UI consumers.
Test with realistic fixtures. Add feature tests for empty strings, identical content, large paragraphs, Unicode, CRLF input, and HTML-like user content. Snapshot the generated HTML only for stable, trusted fixtures.
< noise. If it does not, escaping only the raw input before diffing can break highlighting. Test with <script> and <b> content..text-diff container and scope styles to avoid collisions with your admin theme.TextDiffService or a Blade component rather than scattering package calls across controllers.How can I help you explore Laravel packages today?