Examples

Interactive demos showing Pendu in action.

Dynamic Add & Remove

Adjust the slider to add or remove images. The gallery animates each transition using FLIP animations.

DynamicGallery.tsx
const [photos, setPhotos] = useState(allPhotos);

// Add
setPhotos([...photos, newPhoto]);

// Remove
setPhotos(photos.filter(p => p.id !== removeId));

// The gallery animates automatically
<Pendu gap={12}>
  {photos.map(p => (
    <Pendu.Image key={p.id} {...p} />
  ))}
</Pendu>

Gap & Seed

Control spacing and layout randomization. Same seed always produces the same layout.

Gallery.tsx
<Pendu gap={12} seed={42}>
  <Pendu.Image src="/photo.jpg" width={1200} height={800} alt="Photo" />
  <Pendu.Image src="/portrait.jpg" width={800} height={1200} alt="Portrait" />
</Pendu>

Container Modes

Pendu fills whatever container you give it — fixed height, percentage, or viewport units.

Fixed 300px height

Dynamic height (auto)

ContainerModes.tsx
{/* Fixed height — gallery scales to fit */}
<div style={{ height: 400 }}>
  <Pendu gap={8}>{...}</Pendu>
</div>

{/* Dynamic — gallery grows with content */}
<div style={{ width: '100%' }}>
  <Pendu gap={8}>{...}</Pendu>
</div>

{/* Viewport units */}
<div style={{ height: '60vh', width: '80vw' }}>
  <Pendu gap={8}>{...}</Pendu>
</div>

Size Constraints

Control minimum and maximum frame widths. Useful for keeping portrait images visible and wide panoramas in check.

SizeConstraints.tsx
<Pendu gap={12} minItemWidth={120} maxItemWidth={400}>
  <Pendu.Image src="/portrait.jpg" width={800} height={1200} />
  <Pendu.Image src="/panorama.jpg" width={2400} height={600} />
</Pendu>

Layout Callback

Subscribe to layout changes with onLayoutChange. Useful for lightboxes, tooltips, or analytics.

LayoutCallback.tsx
<Pendu gap={12} onLayoutChange={(result) => {
  console.log(`Placed ${result.stats.placed} frames`);
  console.log('Bounds:', result.bounds);
}}>
  <Pendu.Image src="/photo.jpg" width={1200} height={800} />
</Pendu>

Lazy Loading

Enable native browser lazy loading with a single prop. Images below the fold load on demand, keeping initial page load fast.

LazyLoading.tsx
// All images lazy-loaded
<Pendu lazy gap={12}>
  <Pendu.Image src="/photo.jpg" width={1200} height={800} alt="Photo" />
</Pendu>

// Per-image control
<Pendu gap={12}>
  <Pendu.Image src="/hero.jpg" width={1200} height={800} loading="eager" />
  <Pendu.Image src="/below.jpg" width={800} height={1200} loading="lazy" />
</Pendu>