Documentation
Everything you need to use Pendu in your React application.
Installation
npm install @inkorange/penduimport { Pendu } from '@inkorange/pendu';Requires React 18+ as a peer dependency. Works with Next.js, Vite, CRA, and any React setup.
<Pendu> Props
| Prop | Type | Default | Description |
|---|---|---|---|
| gap | number | 8 | Space between images in pixels |
| seed | number | 42 | Random seed for deterministic layouts. Same seed = same layout. |
| padding | number | 0 | Inner padding of the gallery container |
| className | string | — | CSS class applied to the gallery root element |
<Pendu.Image> Props
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | required | Image source URL |
| width | number | required | Original image width (for aspect ratio) |
| height | number | required | Original image height (for aspect ratio) |
| alt | string | "" | Alt text for accessibility |
<Pendu.Item> Props
Use <Pendu.Item> for custom content — videos, cards, CTAs, or any React component. Provide width and height for aspect ratio calculation; the layout engine handles sizing and positioning.
| Prop | Type | Default | Description |
|---|---|---|---|
| width | number | required | Desired width (for aspect ratio calculation) |
| height | number | required | Desired height (for aspect ratio calculation) |
| children | ReactNode | required | Content to render inside the frame |
| className | string | — | CSS class on the item wrapper |
import { Pendu } from '@inkorange/pendu';
function MixedGallery() {
return (
<Pendu gap={12}>
<Pendu.Image src="/photo.jpg" width={1200} height={800} alt="Photo" />
<Pendu.Item width={400} height={300}>
<video src="/clip.mp4" autoPlay muted loop
style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
</Pendu.Item>
<Pendu.Item width={400} height={400}>
<div style={{ padding: 24, background: '#1a1a2e', color: '#fff' }}>
<h3>New Collection</h3>
<p>Explore the latest additions</p>
</div>
</Pendu.Item>
</Pendu>
);
}CSS Variables
Override styles without props by setting CSS custom properties on the gallery or any ancestor.
| Variable | Default | Description |
|---|---|---|
| --pendu-gap | 8px | Override gap between images |
| --pendu-radius | 0 | Border radius on image frames |
| --pendu-bg | transparent | Background color of the gallery |
| --pendu-transition | 0.4s ease | Transition timing for FLIP animations |
.my-gallery {
--pendu-gap: 16px;
--pendu-radius: 8px;
--pendu-bg: #111;
--pendu-transition: 0.6s cubic-bezier(0.25, 0.1, 0.25, 1);
}
<Pendu className="my-gallery" gap={16}>
{/* images */}
</Pendu>Dynamic Images
Pendu reacts to children changes automatically. Add or remove <Pendu.Image> or <Pendu.Item> elements and the gallery re-layouts with smooth FLIP animations. Use stable key props so React can track each element.
import { useState } from 'react';
import { Pendu } from '@inkorange/pendu';
function PhotoManager() {
const [photos, setPhotos] = useState(initialPhotos);
const addPhoto = (photo) => {
setPhotos(prev => [...prev, photo]);
};
const removePhoto = (id) => {
setPhotos(prev => prev.filter(p => p.id !== id));
};
return (
<Pendu gap={12} seed={7}>
{photos.map(photo => (
<Pendu.Image
key={photo.id}
src={photo.src}
width={photo.width}
height={photo.height}
alt={photo.alt}
/>
))}
</Pendu>
);
}Container Behavior
Dynamic height (default): The gallery grows vertically to fit all images. The container only needs a width.
Fixed height: When the parent has a fixed height (px, vh, %), the gallery scales the entire layout to fit both width and height constraints. More images in a smaller area means smaller images.
Responsive: Pendu observes container resizes and re-layouts automatically. No additional code needed.