A luxury 3D circular carousel component for React with smooth animations, drag interactions, and premium visual effects.
- 🎨 Luxury 3D Effects - Smooth perspective transforms, depth perception, and premium visual effects
- 🖱️ Interactive - Drag, touch, and momentum physics for natural interactions
- ⚡ High Performance - GPU-accelerated animations, 60fps on desktop, 55fps+ on mobile
- 🖼️ Built-in Lightbox - Full-screen image viewer with smooth transitions
- 🎯 LQIP Support - Progressive image loading with blur placeholders
- ⌨️ Keyboard Navigation - Enhanced keyboard support for carousel and lightbox (Arrow keys, ESC) with configurable options
- 📱 Fully Responsive - Touch-optimized for mobile devices
- 🔧 Highly Customizable - Extensive API for fine-tuning every aspect
- 💪 TypeScript - Full type safety and IntelliSense support
- 🌟 Zero Dependencies - Only React as peer dependency
Unlike traditional flat carousels, this component provides:
- ✨ Premium Feel: 3D transforms and smooth physics-based interactions
- 🚀 Production Ready: Used in real projects, battle-tested
- 📦 Zero Config: Works out of the box with sensible defaults
- 🎨 Fully Customizable: 30+ props to fine-tune every aspect
- 📱 Mobile First: Touch-optimized with momentum scrolling
- ♿ Accessible: ARIA labels, keyboard navigation, screen reader friendly
- ⚡ Performant: GPU-accelerated, 60fps on desktop
- Why Choose This?
- Demo
- Installation
- Quick Start
- Use Cases & Examples
- API Reference
- Framework Integration
- Troubleshooting
- Best Practices
- Architecture
- FAQ
- Contributing
- License
- Contact
Live Demo - See it in action!
Interactive 3D carousel with smooth animations and lightbox
# npm
npm install @ddevkim/carousel-circular-3d
# yarn
yarn add @ddevkim/carousel-circular-3d
# pnpm
pnpm add @ddevkim/carousel-circular-3dImportant: You must import the CSS file for styles to work properly!
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css"; // Required for stylesimport { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
const items = [
{ id: 1, image: "/image1.jpg", alt: "Image 1" },
{ id: 2, image: "/image2.jpg", alt: "Image 2" },
{ id: 3, image: "/image3.jpg", alt: "Image 3" },
];
function App() {
return <CarouselCircular items={items} />;
}That's it! 🎉 The carousel will render with sensible defaults.
For a premium look with 3D depth and smooth interactions:
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
function App() {
return (
<CarouselCircular
items={items}
containerHeight={600}
geometry={{
radius: 900,
cameraAngle: 12,
}}
visualEffect={{
minScale: 0.5,
enableReflection: true,
}}
/>
);
}| Prop | Type | Description |
|---|---|---|
items |
CarouselItem[] |
Array of items to display (max 30 recommended) |
| Prop | Type | Default | Description |
|---|---|---|---|
radius |
number |
600 |
Circle radius in pixels |
perspective |
number |
radius * 3.33 |
Perspective depth (min: radius * 2) |
cameraAngle |
number |
0 |
Camera vertical angle (-30 to 30 degrees) |
depthIntensity |
number |
0 |
Individual item Z-depth variation (0-3, recommended: 1.0-2.0) |
| Prop | Type | Default | Description |
|---|---|---|---|
dragSensitivity |
number |
1.0 |
Drag responsiveness multiplier |
enableMomentum |
boolean |
true |
Enable physics-based momentum |
momentumFriction |
number |
0.95 |
Friction coefficient (0-1) |
enableKeyboardNavigation |
boolean |
true |
Enable keyboard navigation (Arrow keys). Auto-disabled when lightbox is open |
| Prop | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
false |
Enable auto-rotation |
speed |
number |
0.1 |
Rotation speed (degrees/frame) |
resumeDelay |
number |
3000 |
Delay before resuming after interaction (ms) |
| Prop | Type | Default | Description |
|---|---|---|---|
opacityRange |
[number, number] |
[0.3, 1.0] |
Opacity range [min, max] |
minScale |
number |
0.7 |
Minimum scale for back items (0.0~1.0, front items are always 1.0) |
enableReflection |
boolean |
false |
Enable bottom reflection effect |
| Prop | Type | Description |
|---|---|---|
className |
string |
CSS class for container |
itemClassName |
string |
CSS class for each item |
| Prop | Type | Default | Description |
|---|---|---|---|
containerHeight |
number |
600 |
Container height in pixels |
| Prop | Type | Default | Description |
|---|---|---|---|
enableLightboxWhenClick |
boolean |
false |
Enable lightbox on click |
lightboxOptions |
LightboxOptions |
- | Lightbox configuration |
LightboxOptions:
| Prop | Type | Default | Description |
|---|---|---|---|
enableKeyboardNavigation |
boolean |
true |
Enable keyboard navigation (ArrowLeft/ArrowRight) in lightbox |
closeOnEsc |
boolean |
true |
Enable ESC key to close lightbox |
backgroundBlur |
number |
8 |
Background blur intensity in pixels |
animationDuration |
number |
500 |
Animation duration in milliseconds |
onOpen |
function |
- | Callback when lightbox opens (receives index) |
onClose |
function |
- | Callback when lightbox closes |
| Prop | Type | Description |
|---|---|---|
onItemClick |
(item, index) => void |
Callback when item is clicked |
ariaLabel |
string |
ARIA label for screen readers |
// Image-based item
type CarouselItemWithImage = {
id: string | number;
image: string;
lqip?: {
base64: string;
width: number;
height: number;
};
alt?: string;
title?: string;
};
// Custom content item
type CarouselItemWithContent = {
id: string | number;
content: ReactNode;
alt?: string;
title?: string;
};
type CarouselItem = CarouselItemWithImage | CarouselItemWithContent;Perfect for e-commerce sites, portfolios, or any image-heavy application:
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
const products = [
{ id: 1, image: "/products/watch.jpg", alt: "Luxury Watch" },
{ id: 2, image: "/products/phone.jpg", alt: "Smartphone" },
{ id: 3, image: "/products/laptop.jpg", alt: "Laptop" },
];
export function ProductGallery() {
return (
<CarouselCircular
items={products}
enableLightboxWhenClick={true}
lightboxOptions={{
enableKeyboardNavigation: true,
closeOnEsc: true,
backgroundBlur: 10,
}}
geometry={{
radius: 900,
cameraAngle: 12,
}}
/>
);
}Features:
- Click any product to view full-screen
- Navigate with arrow keys or swipe
- Press ESC to close
- Smooth fade transitions
Show blurred placeholders instantly while high-res images load:
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
const items = [
{
id: 1,
image: "/gallery/hero.jpg",
lqip: {
base64: "data:image/webp;base64,UklGRiQAAABXRUJQ...", // tiny base64 image
width: 800,
height: 600,
},
alt: "Mountain landscape",
},
];
<CarouselCircular items={items} />;Benefits:
- Instant visual feedback (no blank space)
- Smooth transition from blur to sharp
- Better perceived performance
- Works offline (base64 inline)
Great for landing pages or showcases:
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
export function HeroCarousel() {
return (
<CarouselCircular
items={items}
autoRotateConfig={{
enabled: true,
speed: 0.1, // Slow, elegant rotation
resumeDelay: 3000, // Resume 3s after user interaction
}}
geometry={{
radius: 1200,
cameraAngle: 15,
}}
/>
);
}Pro Tips:
- Use slower speed (0.05-0.15) for luxury feel
- Longer
resumeDelaygives users control - Rotation pauses on hover/drag automatically
Not just images! Render any React component:
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
const teamMembers = [
{
id: 1,
content: (
<div className="team-card">
<img src="/avatar1.jpg" alt="John Doe" />
<h3>John Doe</h3>
<p>CEO & Founder</p>
<div className="social-links">{/* Your custom content */}</div>
</div>
),
},
];
export function TeamCarousel() {
return (
<CarouselCircular
items={teamMembers}
containerHeight={500}
itemClassName="custom-team-card"
/>
);
}Use Cases:
- Team members
- Testimonials
- Feature cards
- Pricing tiers
- Anything you can imagine!
import { useState } from "react";
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
export function ProductShowcase() {
const [selectedProduct, setSelectedProduct] = useState(null);
const products = [
{
id: 1,
image: "/products/watch.jpg",
lqip: { base64: "...", width: 800, height: 600 },
alt: "Premium Watch",
title: "Swiss Automatic Watch",
},
// ... more products
];
return (
<div className="showcase">
<CarouselCircular
items={products}
containerHeight={700}
geometry={{
radius: 1000,
cameraAngle: 12,
depthIntensity: 1.5,
}}
visualEffect={{
minScale: 0.6,
opacityRange: [0.4, 1],
enableReflection: true,
}}
enableLightboxWhenClick={true}
lightboxOptions={{
onOpen: (index) => setSelectedProduct(products[index]),
onClose: () => setSelectedProduct(null),
}}
autoRotateConfig={{
enabled: true,
speed: 0.08,
resumeDelay: 4000,
}}
onItemClick={(item, index) => {
console.log("Clicked:", item.title);
}}
ariaLabel="Product showcase carousel"
/>
{selectedProduct && (
<div className="product-info">
<h2>{selectedProduct.title}</h2>
{/* Additional product details */}
</div>
)}
</div>
);
}- Image Optimization: Use optimized images (WebP, compressed JPEG)
- LQIP: Include LQIP data for smooth loading experience
- Item Count: Keep items under 30 for optimal performance
- Container Height: Match your content size to avoid unnecessary scaling
- Radius: Larger radius (900-1200px) creates more luxurious spacing
- Camera Angle: 8-15 degrees provides subtle depth without distortion
- Depth Intensity: 1.0-2.0 adds subtle Z-axis variation
- Scale Range:
[0.5, 1]creates dramatic focus effect
- Always provide
alttext for images - Set meaningful
ariaLabelfor container - Test keyboard navigation:
- Carousel: Arrow keys (Left/Right) for navigation
- Lightbox: Arrow keys (Left/Right) for navigation, ESC to close
- Keyboard navigation is automatically disabled for carousel when lightbox is open to prevent conflicts
- Ensure sufficient color contrast for text overlays
- Configure keyboard options via
interaction.enableKeyboardNavigationandlightboxOptions.enableKeyboardNavigationif needed
// app/components/ProductCarousel.tsx
"use client"; // Required for client-side interactivity
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
export function ProductCarousel({ items }) {
return <CarouselCircular items={items} />;
}// pages/index.tsx
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
export default function Home() {
return <CarouselCircular items={items} />;
}// src/App.tsx
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
export default function App() {
return <CarouselCircular items={items} />;
}// src/App.js
import { CarouselCircular } from "@ddevkim/carousel-circular-3d";
import "@ddevkim/carousel-circular-3d/dist/index.css";
function App() {
return <CarouselCircular items={items} />;
}Problem: Carousel renders but has no styles (buttons missing, layout broken)
Solution: Make sure you import the CSS file:
import "@ddevkim/carousel-circular-3d/dist/index.css"; // ← Don't forget this!Alternative imports that work:
// Option 1: Direct path (recommended)
import "@ddevkim/carousel-circular-3d/dist/index.css";
// Option 2: Using styles export
import "@ddevkim/carousel-circular-3d/styles";Problem: Type errors when using the component
Solution: The package includes TypeScript definitions. Make sure your tsconfig.json includes:
{
"compilerOptions": {
"moduleResolution": "bundler", // or "node"
"esModuleInterop": true
}
}Problem: Images show broken icon or don't load
Solution:
- Check image paths: Ensure paths are correct relative to your public folder
- Use absolute URLs: Try using full URLs:
https://example.com/image.jpg - Verify CORS: If using external images, ensure CORS headers are set
// ✅ Good - relative to public folder
{ id: 1, image: "/images/product.jpg" }
// ✅ Good - absolute URL
{ id: 1, image: "https://cdn.example.com/product.jpg" }
// ❌ Bad - relative to component file
{ id: 1, image: "../assets/product.jpg" }Problem: Carousel feels laggy or slow
Solutions:
- Reduce item count: Keep items under 30
- Optimize images: Use WebP format and compress
- Add LQIP: Use progressive loading
- Check radius: Very large radius (>2000px) may impact performance
// ✅ Optimized
<CarouselCircular
items={items.slice(0, 20)} // Limit items
geometry={{ radius: 900 }} // Reasonable radius
/>Problem: Click on image but lightbox doesn't open
Solution: Make sure enableLightboxWhenClick is set to true:
<CarouselCircular
items={items}
enableLightboxWhenClick={true} // ← Required for lightbox
/>Problem: Carousel doesn't auto-rotate
Solution: Check your config:
<CarouselCircular
items={items}
autoRotateConfig={{
enabled: true, // ← Must be true
speed: 0.1,
}}
/>Problem: Arrow keys don't work
Solutions:
- Carousel navigation: Ensure
interaction.enableKeyboardNavigationistrue(default) - Lightbox navigation: Check
lightboxOptions.enableKeyboardNavigationistrue(default) - Focus state: Carousel container must be focused (click on it first)
<CarouselCircular
items={items}
interaction={{
enableKeyboardNavigation: true, // Carousel arrow keys
}}
lightboxOptions={{
enableKeyboardNavigation: true, // Lightbox arrow keys
closeOnEsc: true, // ESC to close
}}
/>- GPU-Accelerated: Uses CSS
transformandwill-changefor 60fps animations - Physics-Based: Realistic momentum with configurable friction
- Orientation Detection: Automatically calculates item sizes based on image dimensions
- Smart Angle Distribution: Even spacing regardless of portrait/landscape mix
- RAF-Based: All animations use
requestAnimationFramefor smooth performance
Yes! The package includes full TypeScript definitions with IntelliSense support.
Yes! It works with Next.js (both App Router and Pages Router), including SSR and SSG.
For Next.js App Router, use the "use client" directive in your component file.
You can use any LQIP generation tool. Here are popular options:
- sqip - SVG-based LQIP generator
- lqip-modern - Modern LQIP with multiple algorithms
- plaiceholder - Next.js-friendly LQIP generator
- ImageMagick - Create base64 thumbnails with blur
Example with ImageMagick:
convert input.jpg -resize 20x -quality 30 -blur 0x1 output.webp
base64 output.webpThe lightbox uses minimal inline styles. You can customize it by overriding CSS classes or using className props.
Modern browsers (Chrome, Firefox, Safari, Edge) with CSS3 transforms support. IE11 is not supported.
We recommend keeping items under 30 for optimal performance. The component can handle more, but performance may degrade on lower-end devices.
Currently, the component is optimized for images. For videos, use the content prop with a custom video player component.
We welcome contributions from the community! Whether it's bug reports, feature requests, or code contributions, we appreciate your help in making this library better.
- 🐛 Report Bugs: Use our Bug Report template
- ✨ Request Features: Use our Feature Request template
- ❓ Ask Questions: Use our Question template
- 💻 Submit Pull Requests: Check our Contributing Guide
# Fork and clone the repository
git clone https://github.com/YOUR_USERNAME/carousel-circular-3d.git
cd carousel-circular-3d
# Install dependencies
pnpm install
# Start development
pnpm dev
# Run tests
pnpm lint && pnpm type-check && pnpm buildFor detailed guidelines, please read our Contributing Guide and Code of Conduct.
- Be respectful and inclusive
- Provide constructive feedback
- Help others when you can
- Follow our Code of Conduct
MIT © ddevkim
Built with:
- React 18+ for modern UI
- TypeScript for type safety
- tsup for blazing-fast builds
- Turbo for monorepo management
Have questions or need support?
- 🌐 Website: https://ddev.kim
- 💼 GitHub: ddevkim/carousel-circular-3d
- 📦 npm: @ddevkim/carousel-circular-3d
- 🐛 Issues: Report a bug or request a feature
Made with ❤️ by ddevkim