Skip to main content

preview panel

A pan-zoom container with controls, fullscreen dialog, and synced state between views.

Features

  • Pan & zoom via drag, scroll wheel, and pinch-to-zoom gestures
  • Built-in zoom controls with percentage badge
  • Zero re-renders during continuous interactions (ref-based transforms)
  • html prop for rendering raw HTML/SVG content directly
  • Fullscreen dialog variant with PreviewPanelDialog
  • Synced state between inline and dialog panels via syncPanels
  • RAF-batched state emission for maximum performance
  • Built with gentleduck/ui components: Button, Badge, ButtonGroup, Separator, Tooltip, Dialog

Philosophy

Preview panels bridge the gap between list views and detail views. Instead of navigating away to see content, users get an inline preview that respects their current context. We ship this as a compound component because the trigger, content, and dialog modes each need independent control while sharing state.

How It's Built

Loading diagram...

Installation

npx @gentleduck/cli add preview-panel
npx @gentleduck/cli add preview-panel

Usage

import { PreviewPanel } from '@/components/ui/preview-panel'
import { PreviewPanel } from '@/components/ui/preview-panel'
<PreviewPanel maxHeight="400px">
  <YourContent />
</PreviewPanel>
<PreviewPanel maxHeight="400px">
  <YourContent />
</PreviewPanel>

With HTML content

<PreviewPanel html="<svg>...</svg>" maxHeight="400px" />
<PreviewPanel html="<svg>...</svg>" maxHeight="400px" />

With dialog and sync

import { PreviewPanelDialog } from '@/components/ui/preview-panel'
import { PreviewPanelDialog } from '@/components/ui/preview-panel'
<PreviewPanelDialog syncPanels maxHeight="400px">
  <YourContent />
</PreviewPanelDialog>
<PreviewPanelDialog syncPanels maxHeight="400px">
  <YourContent />
</PreviewPanelDialog>

Examples

HTML / SVG content

Render raw HTML or SVG strings directly using the html prop.

Dialog with synced zoom

Open the content in a fullscreen dialog. Zoom and position sync between both panels.

Image viewer

Use as a zoomable image viewer.

API Reference

PreviewPanel

The core pan-zoom container.

PropTypeDefaultDescription
maxHeightstring--Maximum height of the panel container (e.g. "400px")
minZoomnumber0.25Minimum zoom level
maxZoomnumber4Maximum zoom level
initialZoomnumber1Starting zoom level
showControlsbooleantrueWhether to show the zoom controls overlay
htmlstring--Raw HTML string to render inside the panel. Takes priority over children
childrenReact.ReactNode--Content to render inside the panel
classNamestring--Additional CSS classes for the outer container
styleReact.CSSProperties--Inline styles for the outer container
onStateChange(state: PreviewPanelState) => void--Called whenever zoom or position changes. Use to sync with another panel
syncStatePreviewPanelState--External state to apply. The panel syncs to this state when set
...propsReact.HTMLProps<HTMLDivElement>--Additional props to spread to the content div

PreviewPanelDialog

Combines an inline PreviewPanel with a fullscreen Dialog. Includes an expand button in the bottom-right corner.

PropTypeDefaultDescription
childrenReact.ReactNode--Content rendered in both inline and dialog panels
htmlstring--Raw HTML string. Takes priority over children
classNamestring--Class name for the outer wrapper
panelClassNamestring--Class name applied to both PreviewPanel instances
maxHeightstring--Maximum height of the inline panel
minZoomnumber0.25Minimum zoom level
maxZoomnumber4Maximum zoom level
initialZoomnumber1Starting zoom level
showControlsbooleantrueWhether to show zoom controls
syncPanelsbooleantrueWhether to sync zoom and position between inline and dialog panels

PreviewPanelState

The state object used for syncing between panels.

type PreviewPanelState = {
  zoom: number
  x: number
  y: number
}
type PreviewPanelState = {
  zoom: number
  x: number
  y: number
}

RTL Support

Direction is resolved through the shared primitives direction module. Use a local dir="rtl" override when the component exposes it, or set DirectionProvider at app/root level for global RTL/LTR behavior.