Pendu

Organic gallery layouts for React

A lightweight component that arranges images into beautiful, organic collages. No grid. No masonry. Just art.

npm install @inkorange/pendu

Why Pendu?

Organic Layouts

No grid lines, no rigid columns. Images arrange themselves into natural, gallery-wall collages that fill your container.

Animated Transitions

FLIP animations smoothly move images when the gallery changes. Add, remove, or reorder — every transition feels intentional.

Container Aware

Automatically adapts to any container size — fixed, percentage, or viewport units. Images scale and reflow to fill the space.

5.4 KB Gzipped

Zero runtime dependencies. Only 6 files installed — nothing beyond React as a peer dependency. Ships ESM and CJS with full TypeScript types.

CSS Variable Theming

Customize gap, radius, and background via CSS custom properties. No prop drilling needed.

Deterministic Seeds

Same seed + same images = identical layout. Reproducible across renders, servers, and sessions.

Get started in seconds

Basic Usage

Gallery.tsx
import { Pendu } from '@inkorange/pendu';

function Gallery() {
  return (
    <Pendu gap={12} seed={42}>
      <Pendu.Image src="/photo-1.jpg" width={1200} height={800} alt="Sunset" />
      <Pendu.Image src="/photo-2.jpg" width={800} height={1200} alt="Portrait" />
      <Pendu.Image src="/photo-3.jpg" width={1600} height={1000} alt="Landscape" />
    </Pendu>
  );
}

Dynamic Arrays

DynamicGallery.tsx
import { Pendu } from '@inkorange/pendu';
import { useState } from 'react';

function DynamicGallery({ photos }) {
  return (
    <Pendu gap={12}>
      {photos.map((photo) => (
        <Pendu.Image
          key={photo.id}
          src={photo.src}
          width={photo.width}
          height={photo.height}
          alt={photo.alt}
        />
      ))}
    </Pendu>
  );
}

CSS Variable Theming

styles.css
/* Override via CSS variables — no props needed */
.my-gallery {
  --pendu-gap: 16px;
  --pendu-radius: 8px;
  --pendu-bg: #1a1a1a;
}

Ready to build?

Explore interactive examples or dive into the API docs.