A React slideshow component with stunning 3D transitions powered by Three.js
npm install @dylanwells/react-3d-slideshow> Warning: This library is a work in progress and has not been thoroughly tested. Use at your own risk in production environments.
A React slideshow component with 3D transitions powered by Three.js and @react-three/fiber.
``bash`
npm install @dylanwells/react-3d-slideshow
`tsx
import { Slideshow } from '@dylanwells/react-3d-slideshow'
const slides = [
{ id: 1, image: '/image1.jpg' },
{ id: 2, image: '/image2.jpg' },
{ id: 3, image: '/image3.jpg' },
]
function App() {
return (
style="glitch"
aspectRatio={16 / 9}
autoPlay
loop
/>
)
}
`
- glitch - Chromatic aberration, scanlines, and film grain effects
- cascade - 3D cube grid with diagonal wave animation
- cube - Simple 3D cube rotation between slides
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| slides | SlideData[] | required | Array of slide objects with id and image properties |style
| | 'glitch' \| 'cascade' \| 'cube' | 'cascade' | Transition style to use |transitionDuration
| | number | 800 | Duration of transitions in milliseconds |aspectRatio
| | number | 1.5 (3:2) | Aspect ratio for the slideshow display area |fullscreen
| | boolean | false | Fill the entire viewport (glitch and cascade only) |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| autoPlay | boolean | false | Automatically advance slides |autoPlayInterval
| | number | 5000 | Time between auto-advances in milliseconds |pauseOnHover
| | boolean | true | Pause autoplay when hovering over the slideshow |loop
| | boolean | true | Loop back to first slide after reaching the end |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| width | string \| number | '100%' | Width of the slideshow container |height
| | string \| number | 400 | Height of the slideshow container |initialSlide
| | number | 0 | Index of the initial slide to display |className
| | string | - | CSS class for the container element |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| showControls | boolean | true | Show prev/next navigation buttons |showIndicators
| | boolean | true | Show slide indicator dots |enableSwipe
| | boolean | true | Enable touch swipe and mouse drag navigation |enableKeyboard
| | boolean | true | Enable arrow key navigation |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| loadingSpinner | ReactElement \| null | default spinner | Custom loading spinner. Set to null to hide |prevButton
| | ReactElement \| null | default button | Custom previous button. Set to null to hide |nextButton
| | ReactElement \| null | default button | Custom next button. Set to null to hide |renderIndicator
| | (index: number, isActive: boolean) => ReactElement | - | Custom indicator renderer |focusRingStyles
| | FocusRingStyles | - | Customize keyboard focus ring appearance |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| glitchAberration | number | 0.5 | Chromatic aberration intensity (0-1) |glitchScanlines
| | number | 0.5 | Scanlines effect intensity (0-1) |glitchGrain
| | number | 0.5 | Film grain effect intensity (0-1) |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| cascadeMinTiles | number | 10 | Minimum number of tiles in the shorter dimension |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| ariaLabel | string | 'Image slideshow' | Accessible label for the slideshow region |getSlideAriaLabel
| | (index: number, total: number) => string | - | Custom function to generate slide announcements |focusRingStyles
| | FocusRingStyles | - | Customize focus ring color, width, and offset |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| fallback | ReactElement \| null | crossfade slideshow | Custom fallback when WebGL is unavailable. Set to null to show nothing |onWebGLUnsupported
| | () => void | - | Callback fired when WebGL is not supported |
| Prop | Type | Description |
|------|------|-------------|
| onSlideChange | (index: number) => void | Called when the active slide changes |onWebGLUnsupported
| | () => void | Called when WebGL is not available |
`tsx`
prevButton={
nextButton={
/>
Set to null to hide a button entirely:
`tsx`
prevButton={null} // Hide prev button
nextButton={
/>
`tsx`
renderIndicator={(index, isActive) => (
)}
/>
`tsx
import { LoadingSpinner } from '@dylanwells/react-3d-slideshow'
// Use built-in spinner with custom props
loadingSpinner={
/>
// Or use your own component
loadingSpinner={
/>
// Or hide it entirely
loadingSpinner={null}
/>
`
Customize the keyboard focus ring for accessibility:
`tsx`
focusRingStyles={{
color: '#00ff00', // Focus ring color (default: '#fff')
width: 3, // Ring width in pixels (default: 2)
offset: 4, // Ring offset in pixels (default: 2)
}}
/>
When WebGL is not supported, the slideshow automatically falls back to a simple crossfade effect. You can customize this behavior:
`tsx
// Use default crossfade fallback (default behavior)
// Custom fallback component
fallback={
onWebGLUnsupported={() => console.log('WebGL not available')}
/>
// Show nothing when WebGL unavailable
fallback={null}
/>
`
You can also check WebGL support programmatically:
`tsx
import { isWebGLSupported } from '@dylanwells/react-3d-slideshow'
if (isWebGLSupported()) {
// WebGL is available
}
`
The Slideshow component exposes methods via a ref for programmatic control:
`tsx
import { useRef } from 'react'
import { Slideshow, SlideshowHandle } from '@dylanwells/react-3d-slideshow'
function App() {
const slideshowRef = useRef
return (
<>
>
)
}
`
| Method | Description |
|--------|-------------|
| next() | Advance to the next slide |prev()
| | Go to the previous slide |goTo(index: number)
| | Jump to a specific slide by index |getCurrentIndex()
| | Get the current slide index |
The component uses BEM-style class names for styling customization:
| Class | Description |
|-------|-------------|
| .r3dss | Root container |.r3dss--cascade
| | Root with cascade style |.r3dss--cube
| | Root with cube style |.r3dss--glitch
| | Root with glitch style |.r3dss--empty
| | Root when no slides provided |.r3dss__canvas
| | Three.js canvas element |.r3dss__controls
| | Navigation controls container |.r3dss__control
| | Navigation button |.r3dss__control--prev
| | Previous button |.r3dss__control--next
| | Next button |.r3dss__control--disabled
| | Disabled button state |.r3dss__control--hovered
| | Hovered button state |.r3dss__control--custom
| | Custom button element |.r3dss__indicators
| | Indicator dots container |.r3dss__indicator
| | Individual indicator dot |.r3dss__indicator--active
| | Active indicator |.r3dss__indicator--custom
| | Custom indicator element |.r3dss__loader
| | Loading spinner container |.r3dss__spinner
| | Default loading spinner |.r3dss__live-region
| | Screen reader announcements |.r3dss__empty-message
| | Empty state message |.r3dss__fallback
| | WebGL fallback container |.r3dss__fallback-slide
| | Fallback slide element |
Each slide object should have the following structure:
`ts`
interface SlideData {
id: string | number // Unique identifier
image?: string // Image URL
content?: ReactNode // Custom React content (not fully supported)
backgroundColor?: string // Fallback background color
}
All TypeScript types are exported for convenience:
`tsx``
import type {
SlideshowProps,
SlideshowHandle,
SlideData,
TransitionStyle,
ControlsProps,
IndicatorsProps,
FocusRingStyles,
LoadingSpinnerProps,
FallbackSlideshowProps,
} from '@dylanwells/react-3d-slideshow'
MIT