Migrating from single-file HTML to a Next.js app with multi-format comparison, local history, and polished UX.
| Question | Decision | Reasoning |
|---|---|---|
| Name | DiffLab | User preference. Existing "DiffLab" uses (AI co, image tool, research lab) don't overlap. |
| Deployment | Vercel (Cloudflare Pages as fallback) | App is mostly client-side. Vercel is native for Next.js. CF Pages works via static export. |
| Analytics | Vercel Analytics | Free on hobby plan, zero config on Vercel, ~1KB script, no cookies, GDPR-compliant. |
| Editor | CodeMirror 6 | Free (MIT), modular (~150KB base), performant. Monaco is ~2MB — overkill. |
| Local DB | Dexie.js | ~29KB gzip. Negligible vs CodeMirror + Next.js. Query-friendly API, good DX. Worth trying. |
| State mgmt | Zustand (if needed) | Only if prop drilling or cross-component state becomes a problem. Not preemptively. |
| Storage limit | Browser-managed + soft warning at 80% | No arbitrary cap. Warn user when approaching quota via navigator.storage.estimate(). |
- Initialize Next.js project (App Router, TypeScript strict, Tailwind CSS v4, Bun)
- Project structure:
src/app,src/components,src/lib,src/hooks,src/stores,src/types - Configure ESLint, Prettier
- Set up path aliases (
@/) - Port existing JSON diff logic into
src/lib/diff/ - Port existing UI into React components
- Remove Tailwind CDN, use proper Tailwind install
- Verify feature parity with current single-file app before moving forward
- JSON (existing — port & improve)
- YAML (
yamlpackage) - TOML (
smol-toml) - Plain text (line-by-line diff)
- Markdown (line-by-line diff, treat as text)
- Code files: JS, TS, Kotlin, Swift, etc. (line-by-line diff, treat as text)
- Structured diff for JSON, YAML, TOML — parse into objects, compare keys/values (current behavior)
- Text diff for everything else — line-by-line with
diff(jsdiff) library - Unified diff view + side-by-side diff view toggle
- Diff summary stats per format
- Detect format from file extension on drop/upload
- Detect format from content heuristics (leading
{/[= JSON,---= YAML,[section]= TOML, etc.) - Format selector dropdown (manual override) per panel
- Type mismatch error: if left panel is JSON and right panel is YAML, show clear error — do not compare
- Allow "Text mode" override to force text diff regardless of format
- Ignore whitespace toggle (for text/code diffs)
- Ignore case toggle
- Ignore comments toggle (for code files — strip
//,/* */,#before diff)
- Replace
<textarea>with CodeMirror 6 editors - Language extensions: JSON, YAML, TOML, Markdown, JavaScript, TypeScript, Kotlin, Swift, HTML, CSS
- Theme that matches app's dark/light mode (custom CodeMirror theme)
- Line numbers
- Code folding
- Bracket matching
- Search within editor (Ctrl+F)
- Format button per panel (pretty-print for structured formats, no-op for text)
- JSON:
JSON.stringifywith indent - YAML:
yaml.stringifywith indent - TOML:
smol-toml.stringify - Format on paste setting (toggle in settings/toolbar) — auto-format when content is pasted
- Minify/compact button for structured formats
- Preserve cursor position after format
- CSS custom properties for both themes (extend current
--bg-base,--text, etc.) - Light theme color palette
- Dark theme (current, refined)
- System preference detection (
prefers-color-scheme) - Manual toggle (dark / light / system) in navbar
- Persist preference in localStorage
- CodeMirror theme sync with app theme
- Smooth transition between themes
- Dexie.js setup with versioned schema
- Schema:
comparisonstable with fields:id(auto-generated UUID)title(auto-generated or user-editable)leftContent,rightContentleftFormat,rightFormatleftFileName,rightFileNamecreatedAt,updatedAttags(optional, for organization)
- Storage quota monitoring via
navigator.storage.estimate()— soft warning at 80%
- History panel/page (
/history) - List view: title, formats, date, preview snippet
- Search/filter history by title, format, date range, tags
- Sort by date (newest/oldest)
- Load a saved comparison back into the editor
- Edit comparison title/tags
- Delete individual comparisons
- Bulk delete / clear all (with confirmation)
- Export history as JSON backup
- Import history from JSON backup
- Option to auto-save comparisons (toggle in settings)
- Save button to manually save current comparison
- Indicator showing "saved" / "unsaved" state
- Dynamic
<title>and<meta description>per page - Open Graph tags (og:title, og:description, og:image, og:url, og:type)
- Twitter Card meta tags
- Canonical URLs
- Structured data (JSON-LD) for the tool
- Favicon (SVG icon)
- OG image (1200x630 branded preview card)
- App icons for PWA manifest
-
robots.txt -
sitemap.xml(generated via Next.js) - Proper heading hierarchy (h1, h2, etc.)
- Semantic HTML throughout
| Vercel Analytics | Firebase/GA4 | Clarity | |
|---|---|---|---|
| Cost | Free (2.5K events/mo) | Free (unlimited) | Free (unlimited) |
| Script size | ~1KB | ~45KB | ~22KB |
| Cookies | No | Yes | Yes |
| GDPR consent | No | Yes | Yes |
| Setup | 1 line | Firebase project + SDK | Script tag |
| Dashboard | Simple, clean | Complex, mobile-focused | Heatmaps, recordings |
| Best for | Web tools | Mobile apps, funnels | UX research |
- Pick analytics provider (Vercel Analytics recommended, decide at launch)
- No tracking of content — only page views, referrers, basic usage
- "Try an example" button/dropdown in toolbar or empty state
- Sample pairs for every supported format:
- JSON: two API responses with added/removed/changed fields
- YAML: two config files (e.g., Kubernetes manifests) with diffs
- TOML: two Cargo.toml or pyproject.toml files with version bumps
- Markdown: two README versions with section changes
- JavaScript/TypeScript: two versions of a function with refactoring
- Kotlin: two data class versions
- Swift: two struct versions
- Plain text: two paragraphs with word changes
- Clicking a sample auto-loads both panels, sets format, and runs diff
- Samples stored as static assets in
src/data/samples/(not fetched from server) - Each sample has a short description shown in the dropdown
- Empty state CTA: "Not sure how it works? Try an example" with a prominent button
- Keyboard shortcuts panel (help modal showing all shortcuts)
- Ctrl+Shift+S swap (existing)
- Ctrl+Shift+F format both
- Ctrl+Shift+D toggle diff/keys view
- Ctrl+S save comparison to history
- ? toggle shortcuts modal
- File upload button (not just drag & drop — some users prefer clicking)
- File download button (formats structured data before download)
- Inline filename editing (double-click to rename in editor header)
- Ignore whitespace toggle for text diffs
- Responsive design audit — mobile, tablet, desktop
- Loading states, empty states, error states for all views
- Toast notification system (improve existing)
- Drag & drop refinements (visual feedback, multi-file handling)
- Copy diff results (existing — extend for all formats)
- Accessibility: ARIA labels, keyboard navigation, focus management, screen reader support
- 404 page
- Settings persistence (format on paste, ignore whitespace — persisted in localStorage)
- Side-by-side diff view toggle (unified + split with toggle buttons)
- Ignore case toggle for text diffs
- Minify/compact button for structured formats (JSON, YAML, TOML)
- Accessibility: ARIA labels, roles, aria-selected, aria-pressed, aria-modal on all interactive elements
- Responsive design: flex-wrap toolbars, modal margins, label truncation
- Analytics integration (Vercel Analytics — decide at launch)
- OG image (1200x630 branded preview card)
- Auto-save toggle
- Ignore comments toggle (strip
//,/* */,#before diff)
- PWA: service worker, offline support, installable
- Import from URL: fetch content from a remote URL into a panel
- Share via link: encode small diffs in URL params or generate a temporary shareable hash (no server — URL encoding or free paste service)
- Web Workers: offload diff computation for large files to a worker thread
- Error recovery: auto-fix common JSON issues (trailing commas, single quotes, unquoted keys) with a "Try to fix" button
- Diff navigation: jump to next/previous change in diff view
- Collapsible sections in structured diff (collapse unchanged nested objects)
- Character-level diff highlighting within changed lines (word diff)
- Export diff: download diff as
.patch,.txt, or.png(screenshot) - Duplicate detection (from original TODO)
- Multiple tabs: concurrent comparisons in separate tabs within the app
- Clipboard paste detection: auto-detect format from clipboard content on paste
- Print-friendly styles for diff output
| Concern | Choice |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript (strict) |
| Runtime | Bun |
| Styling | Tailwind CSS v4 |
| Editor | CodeMirror 6 |
| Diff engine | jsdiff + custom structured |
| YAML parsing | yaml |
| TOML parsing | smol-toml |
| Local DB | Dexie.js (~29KB gz) |
| State management | Zustand (only if needed) |
| Analytics | Vercel Analytics |
| Deployment | Vercel (CF Pages as fallback) |