# Before / After Image Viewer

Before / After Image Viewer [#before--after-image-viewer]

The **Before / After Image Viewer** compares an original image with a transformed result. It supports two modes: a **two-panel grid** (both images side-by-side, the result panel ring-highlighted and rendered on a checkerboard transparency background so alpha shows through), and a segmented **Original / Enhanced toggle** that swaps a single displayed image source.

It is presentational — both `originalSrc` and `processedSrc` are image source strings (data URL or object URL). Produce the result image with [`useImageToImage`](https://localmode.dev/docs/react) (upscaling / super-resolution), whose `UpscaleImageResult.image` is an `ImageData | Blob` you convert to an object URL (`URL.createObjectURL`) before passing it as `processedSrc`. It owns only the toggle's local UI state.

**When to use it:** background removers, photo enhancers / upscalers, segmentation viewers — any "here's your input, here's the result" comparison.

Preview [#preview]

```tsx
'use client';

import * as React from 'react';
import { BeforeAfterImageViewer } from '@/components/before-after-image-viewer';

/** Inline SVG data URL — avoids any network request in the live preview. */
function svgDataUrl(svg: string) {
  return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
}

const ORIGINAL = svgDataUrl(
  `<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300"><rect width="400" height="300" fill="#cbd5e1"/><circle cx="200" cy="150" r="80" fill="#64748b"/><text x="200" y="280" font-family="sans-serif" font-size="20" fill="#334155" text-anchor="middle">original (blurred)</text></svg>`,
);

// A result with real transparency around the subject (checkerboard shows through).
const PROCESSED = svgDataUrl(
  `<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300"><circle cx="200" cy="150" r="80" fill="#0ea5e9"/><text x="200" y="280" font-family="sans-serif" font-size="20" fill="#f8fafc" stroke="#0f172a" stroke-width="0.75" paint-order="stroke" text-anchor="middle">subject (bg removed)</text></svg>`,
);

export default function BeforeAfterImageViewerDemo() {
  const [mode, setMode] = React.useState<'grid' | 'toggle'>('grid');

  return (
    <div className="w-full max-w-xl space-y-3">
      <div className="inline-flex rounded-lg border border-border bg-muted p-1 text-sm">
        <button
          type="button"
          onClick={() => setMode('grid')}
          className={mode === 'grid' ? 'rounded-md bg-background px-3 py-1 font-medium shadow-sm' : 'px-3 py-1 text-muted-foreground'}
        >
          Grid
        </button>
        <button
          type="button"
          onClick={() => setMode('toggle')}
          className={mode === 'toggle' ? 'rounded-md bg-background px-3 py-1 font-medium shadow-sm' : 'px-3 py-1 text-muted-foreground'}
        >
          Toggle
        </button>
      </div>

      <BeforeAfterImageViewer
        originalSrc={ORIGINAL}
        processedSrc={PROCESSED}
        mode={mode}
        processedLabel="Result"
        // Distinct alts so AT never hears the same description for both images.
        originalAlt="Original portrait before background removal"
        resultAlt="Portrait with the background removed"
      />
    </div>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/media-vision/before-after-image-viewer
```

Dependencies [#dependencies]

* **Data source:** renders the `originalSrc` / `processedSrc` image strings you pass — works with any backend that produces a transformed image. Recommended LocalMode producer: `useImageToImage` (upscale / super-resolution) from `@localmode/react` — convert its `image` (`ImageData | Blob`) to an object URL for `processedSrc`. For background removal, composite `useSegmentImage`'s `masks` onto the source into a transparent PNG first (optional).

* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `before-after-image-viewer.tsx` — the component (checkerboard CSS shipped inline)
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**BeforeAfterImageViewer**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `originalSrc` | `string` | — | **Required.** Source (data URL or URL) of the original image. |
| `processedSrc` | `string` | — | Source of the transformed result (upscaled, segmented, background-removed). When omitted, only the original is shown. |
| `mode` | `"toggle" \| "grid"` | `"grid"` | Layout. `"grid"` shows both panels side-by-side; `"toggle"` shows one image with a segmented Original/Enhanced switch. |
| `originalLabel` | `string` | `"Original"` | Label for the original panel/toggle. |
| `processedLabel` | `string` | `"Enhanced"` | Label for the result panel/toggle. |
| `checkerboard` | `boolean` | `true` | Render the result panel on a checkerboard transparency background so alpha shows through (segmentation / background removal). |
| `alt` | `string` | `""` | Base alt text for the subject. Distinct alts are derived for the two images so assistive tech never hears the same description for both — the original becomes `"{originalLabel}: {alt}"` and the result `"{processedLabel}: {alt}"`. Empty (the default) keeps both images decorative. Use {@link originalAlt} / {@link resultAlt} to set them explicitly. |
| `originalAlt` | `string` | — | Explicit alt for the original image. Overrides the value derived from {@link alt}. |
| `resultAlt` | `string` | — | Explicit alt for the processed/result image. Overrides the value derived from {@link alt}. |

Backing hooks [#backing-hooks]

Produce `processedSrc` with `useImageToImage` (upscale / super-resolution) from [`@localmode/react`](https://localmode.dev/docs/react): its `UpscaleImageResult.image` is an `ImageData | Blob`, so wrap it in an object URL (`URL.createObjectURL`) before passing it. For background removal, `useSegmentImage` returns `masks` (not a ready image) — composite a mask onto the source to build a transparent PNG, then pass that URL.

Examples [#examples]

Grid (default) [#grid-default]

```tsx
import { BeforeAfterImageViewer } from '@/components/before-after-image-viewer';
import { useImageToImage } from '@localmode/react';

const { data } = useImageToImage({ model });
// data.image is ImageData | Blob — turn the Blob result into a displayable URL
const processedSrc = data?.image instanceof Blob ? URL.createObjectURL(data.image) : undefined;

<BeforeAfterImageViewer originalSrc={inputDataUrl} processedSrc={processedSrc} />
```

Toggle mode [#toggle-mode]

```tsx
<BeforeAfterImageViewer
  originalSrc={inputDataUrl}
  processedSrc={processedSrc} // object URL from the upscale result Blob
  mode="toggle"
  processedLabel="Upscaled"
/>
```

Background-removal result on checkerboard [#background-removal-result-on-checkerboard]

```tsx
const { data } = useSegmentImage({ model });

// useSegmentImage returns `masks` (SegmentMask[]), not a ready image —
// composite a mask onto the source to produce a transparent-background PNG URL.
const cutoutUrl = data ? compositeMaskToTransparentPng(inputDataUrl, data.masks) : undefined;

<BeforeAfterImageViewer
  originalSrc={inputDataUrl}
  processedSrc={cutoutUrl}
  checkerboard // default — reveals the transparent background
/>
```

Accessibility [#accessibility]

The two images get **distinct** alt text so assistive tech never announces the same description for both. Pass a single `alt` and the viewer derives `"{originalLabel}: {alt}"` for the original and `"{processedLabel}: {alt}"` for the result; or set `originalAlt` / `resultAlt` explicitly for full control. An empty `alt` (the default) keeps both images decorative.

```tsx
<BeforeAfterImageViewer
  originalSrc={inputDataUrl}
  processedSrc={cutoutUrl}
  originalAlt="Original team photo"
  resultAlt="Team photo with the background removed"
/>
```

Customization [#customization]

The checkerboard is a self-contained inline CSS-gradient background, so the component works standalone after `shadcn add` with no global CSS. Adjust the checker size/color in the copied `before-after-image-viewer.tsx`, or set `checkerboard={false}` for opaque results. Panels use shadcn/ui tokens (`border-border`, `bg-card`, `ring-primary`).