@erosnicolau/photo-stack
v0.1.2
Published
Advanced React photo stack component with configurable placement and animations
Maintainers
Readme
@erosnicolau/photo-stack
Advanced React photo stack component with configurable placement, animations, and responsive design.
Features
- 🖼️ Smart Layout - Handles up to 5 images with intelligent default positioning
- 🎨 5 Animation Types - Fade, slide, scale, rotate, fade-slide, and none
- 📐 9 Placement Options - Precise positioning with top/center/bottom + left/center/right combinations
- 🎯 Custom Offsets - Pixel and percentage-based positioning with overflow support
- 🎪 Rotation Support - Rotate images for dynamic, artistic layouts
- 🌗 Shadow Effects - Configurable shadow sizes and opacity
- 📱 Responsive Design - Fluid width/height with percentage and fixed sizing
- ⚡ Performance Optimized - Efficient rendering with intersection observer
- 🚀 Virtualization - Only renders visible content to optimize performance
- 🎭 Mixed Orientations - Handles landscape, portrait, and square images gracefully
- 🔧 Highly Configurable - Extensive customization for padding, alignment, and styling
- 🎬 Animation Timing - Individual animation delays and timeouts
- ♿ Accessible - Semantic HTML with proper image alt attributes
- 🎯 TypeScript - Full type safety with comprehensive interfaces
Installation
npm install @erosnicolau/photo-stackQuick Start
import PhotoStack from '@erosnicolau/photo-stack'
function MyComponent() {
const images = [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
'https://example.com/image3.jpg'
]
return <PhotoStack images={images} width="100%" height={300} />
}Basic Usage
Simple Image URLs
<PhotoStack
images={[
'https://picsum.photos/300/200',
'https://picsum.photos/300/200',
'https://picsum.photos/300/200'
]}
width="100%"
height={400}
/>Mixed Image Orientations
<PhotoStack
images={[
'https://picsum.photos/300/200', // Landscape
'https://picsum.photos/200/300', // Portrait
'https://picsum.photos/250/250' // Square
]}
width="100%"
height={300}
/>Advanced Configuration
Custom Image Configuration
const configuredImages = [
{
url: 'https://picsum.photos/id/42/300/200',
placement: 'center-left',
offsetX: '10px',
rotation: -5,
animation: {
type: 'fade-slide',
timeout: 0
},
shadow: {
size: 'lg',
opacity: 0.3
}
},
{
url: 'https://picsum.photos/id/43/300/200',
placement: 'bottom-right',
offsetY: '-15px',
rotation: 3,
animation: {
type: 'fade-slide',
timeout: 400
}
}
]
<PhotoStack images={configuredImages} width="100%" height={300} />Placement Options
The component supports 9 placement positions:
// Corner placements
'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
// Edge center placements
'top-center' | 'center-left' | 'center-right' | 'bottom-center'
// Center placement
'center'Placement Examples
<PhotoStack
images={[
{ url: 'image1.jpg', placement: 'top-left' },
{ url: 'image2.jpg', placement: 'center' },
{ url: 'image3.jpg', placement: 'bottom-right' }
]}
width={400}
height={300}
/>Animation Types
Available Animations
fade- Fade in effectslide- Slide in from placement directionscale- Scale up from 0rotate- Rotate in with fadefade-slide- Combined fade and slide effectnone- No animation
Animation Configuration
<PhotoStack
images={[
{
url: 'image1.jpg',
placement: 'center',
animation: {
type: 'fade-slide',
duration: 800, // Animation duration in ms
delay: 0, // Animation delay in ms
timeout: 500, // Delay before animation starts
easing: 'ease-out' // CSS easing function
}
}
]}
/>Positioning & Offsets
Pixel Offsets
<PhotoStack
images={[
{
url: 'image1.jpg',
placement: 'center',
offsetX: '50px', // Move right
offsetY: '-30px' // Move up
}
]}
/>Percentage Offsets
<PhotoStack
images={[
{
url: 'image1.jpg',
placement: 'center',
offsetX: '25%', // Move right by 25% of container
offsetY: '-15%' // Move up by 15% of container
}
]}
/>Extreme Offsets (Overflow)
// Push images outside container bounds
<PhotoStack
images={[
{
url: 'image1.jpg',
placement: 'center-left',
offsetX: '-200px' // Push mostly outside left edge
}
]}
/>Rotation & Effects
Image Rotation
<PhotoStack
images={[
{
url: 'image1.jpg',
placement: 'center',
rotation: 15 // Degrees clockwise
},
{
url: 'image2.jpg',
placement: 'top-right',
rotation: -8 // Degrees counterclockwise
}
]}
/>Shadow Effects
<PhotoStack
images={[
{
url: 'image1.jpg',
placement: 'center',
shadow: {
size: '2xl', // 'sm' | 'md' | 'lg' | 'xl' | '2xl'
opacity: 0.4, // 0 to 1
color: '#000000' // Hex color
}
}
]}
/>Layout Options
Container Alignment
<PhotoStack
images={images}
width={300}
height={250}
align="left" // 'left' | 'center' | 'right'
/>Container Padding
<PhotoStack
images={images}
padding="32px" // Fixed padding
/>
<PhotoStack
images={images}
padding="5%" // Percentage padding
/>
<PhotoStack
images={images}
padding="10px 20px" // Horizontal/vertical padding
/>
<PhotoStack
images={images}
padding="5px 10px 15px 20px" // Top right bottom left
/>Responsive Sizing
// Fluid width with fixed height
<PhotoStack
images={images}
width="100%"
height={300}
/>
// Fixed dimensions
<PhotoStack
images={images}
width={400}
height={300}
/>
// Container-relative sizing
<PhotoStack
images={images}
width="80%"
height="60vh"
/>Virtualization
PhotoStack features virtualization that's always enabled - only rendering image content when the component is visible in the viewport. This dramatically improves performance for pages with many PhotoStack components.
How It Works
<PhotoStack images={images} />Virtualization happens automatically with no configuration needed.
Virtualization Behavior
- Always on: Images show as loading placeholders when outside viewport
- Smart margins: 100px margin ensures smooth scrolling experience
- Debug control: Can be disabled globally via localStorage (
debug-virtualization-enabled: false)
Advanced Virtualization Hooks
For custom implementations, you can use the underlying hooks:
import { useVirtualizedIntersectionObserver, useVirtualizationState } from '@erosnicolau/photo-stack'
function CustomComponent() {
const isVirtualizationEnabled = useVirtualizationState()
const { elementRef, isIntersecting, isInViewport } = useVirtualizedIntersectionObserver({
enableVirtualization: isVirtualizationEnabled
})
return (
<div ref={elementRef}>
{isInViewport ? <ExpensiveContent /> : <Placeholder />}
</div>
)
}Styling
Custom CSS Classes
<PhotoStack
images={images}
className="my-custom-stack rounded-lg"
showBorder={true}
/>CSS Variables
The component uses CSS custom properties for theming:
.photo-stack {
--photo-stack-border-color: #e5e7eb;
--photo-stack-border-width: 1px;
--photo-stack-background: transparent;
}API Reference
PhotoStack Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| images | (string \\| ImageConfigInput)[] | required | Array of image URLs or configuration objects |
| width | string \\| number | undefined | Container width (CSS value or pixels) |
| height | string \\| number | undefined | Container height (CSS value or pixels) |
| className | string | '' | Additional CSS classes |
| align | 'left' \\| 'center' \\| 'right' | 'center' | Container alignment |
| showBorder | boolean | false | Show container border |
| padding | string \\| number | undefined | Container padding |
ImageConfigInput Interface
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| url | string | required | Image URL |
| placement | Placement | auto | Position within container |
| offsetX | string \\| number | 0 | Horizontal offset |
| offsetY | string \\| number | 0 | Vertical offset |
| rotation | number | 0 | Rotation in degrees |
| zIndex | number | auto | Stacking order |
| animation | AnimationConfig | default | Animation settings |
| shadow | ShadowConfig | none | Shadow configuration |
Animation Configuration
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| type | AnimationType | 'fade-slide' | Animation type |
| duration | number | 600 | Duration in milliseconds |
| delay | number | 0 | Delay in milliseconds |
| timeout | number | 0 | Start delay in milliseconds |
| easing | string | 'ease-out' | CSS easing function |
Demo
View the comprehensive demo with all features:
import { PhotoStackDemo } from '@erosnicolau/photo-stack/demo'
function App() {
return <PhotoStackDemo />
}Examples
Portfolio Gallery
const portfolioImages = [
{
url: '/portfolio/project1.jpg',
placement: 'center',
animation: { type: 'fade-slide', timeout: 0 }
},
{
url: '/portfolio/project2.jpg',
placement: 'top-right',
rotation: 8,
animation: { type: 'fade-slide', timeout: 200 }
},
{
url: '/portfolio/project3.jpg',
placement: 'bottom-left',
rotation: -5,
animation: { type: 'fade-slide', timeout: 400 }
}
]
<PhotoStack
images={portfolioImages}
width="100%"
height={400}
className="portfolio-stack"
/>Hero Section
<PhotoStack
images={[
{
url: '/hero/background.jpg',
placement: 'center',
animation: { type: 'fade', timeout: 0 }
},
{
url: '/hero/overlay.png',
placement: 'center',
animation: { type: 'scale', timeout: 500 }
}
]}
width="100vw"
height="100vh"
align="center"
/>Creative Layout
const creativeImages = [
{
url: '/creative/1.jpg',
placement: 'top-left',
offsetX: '-100px',
offsetY: '-50px',
rotation: -15,
shadow: { size: 'lg', opacity: 0.3 }
},
{
url: '/creative/2.jpg',
placement: 'bottom-right',
offsetX: '50px',
offsetY: '30px',
rotation: 12,
animation: { type: 'rotate', timeout: 300 }
}
]
<PhotoStack
images={creativeImages}
width={600}
height={400}
padding="20px"
/>License
MIT
Contributing
Issues and pull requests are welcome on GitHub.
