vue-carousel-3d: a 3D carousel that respects Vue 3 conventions
Vue Carousel 3D - Beautiful, flexible and touch supported 3D Carousel for Vue.js
At a glance
- What is it?
- vue-carousel-3d is a Vue component that lays slides out in real 3D with perspective, inverse scaling and directional bias, and it handles touch, pointer, mouse and keyboard input through accessible controls. Version 2 targets Vue 3.5 and above, while Vue 2 users stay on the 1.x line.
- Who is it for?
- vue-carousel-3d fits a Vue 3 codebase that needs a stacked 3D presentation with touch and keyboard handling already solved, and where a flat grid would not read the way you want. It does not fit a content site where a rotating carousel would compete with the text, and the project itself advises using autoplay sparingly and offering disable-3d when motion matters.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Vue, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Real 3D composition rather than a flat list with shadows
The distinguishing feature is that the layout is computed in three dimensions rather than faked with transforms on a flat row. The feature list names four things that shape the composition: perspective, spacing, inverse scaling and directional bias.
Perspective sets the depth of the scene, spacing sets how far apart the slides sit, inverse scaling shrinks slides that are further from the viewer so the stack reads as receding, and directional bias shifts the whole arrangement so the stack leans to one side rather than sitting flat. The API table groups them as perspective, inverseScaling and bias for 3D depth and composition, alongside display, width, height and space for the visible slide count and the geometry.
Those five props are what a designer actually tunes. The default appearance is not something you have to accept, because every element of the composition is a prop rather than a hardcoded value.
The component ships in three module formats, with the package manifest pointing at a CommonJS build, an ES module build, a UMD build for unpkg and jsdelivr, and a types entry for TypeScript. The feature list also calls it an SSR-safe module, which matters for a component library that is likely to be rendered on a server before it hydrates in the browser.
Installing and rendering your first carousel
Installation is a single dependency. The documentation gives both package managers:
pnpm add vue-carousel-3dnpm install vue-carousel-3dThe quick start then registers the two components and renders them:
<template>
<carousel-3d dots controls-visible aria-label="Featured projects">
<slide v-for="(project, index) in projects" :key="project.id" :index="index">
<img :src="project.image" :alt="project.title">
</slide>
</carousel-3d>
</template>
<script setup>
import { Carousel3d, Slide } from 'vue-carousel-3d'
const projects = [
{ id: 1, title: 'Orbit', image: '/images/orbit.jpg' },
{ id: 2, title: 'Depth', image: '/images/depth.jpg' }
]
</script>The props in that first line already show the component's defaults in practice: dots switches on the navigation indicators, controls-visible turns on the built-in arrows, and aria-label gives the carousel an accessible name.
Two rules about slides matter more than they look. Each slide needs a stable, zero-based index, and for dynamic collections you also pass the count so the carousel recomputes its layout immediately:
<template>
<carousel-3d controls-visible aria-label="Featured projects">
<slide v-for="(project, index) in projects" :key="project.id" :index="index">
{{ project.title }}
</slide>
</carousel-3d>
</template>A stable index is what the carousel uses to position a slide, and a zero-based one because the programmatic navigation methods take the same numbering.
Registration styles and the global component option
The component supports the three registration styles a Vue 3 codebase might use.
Local registration inside a single file is the simplest. The script block registers the two components in the components option and the carousel lives in the template:
<script>
import { Carousel3d, Slide } from 'vue-carousel-3d'
export default {
components: { Carousel3d, Slide },
data: () => ({ projects: [] })
}
</script>Global registration is a single install call:
import { createApp } from 'vue'
import Carousel3d from 'vue-carousel-3d'
createApp(App).use(Carousel3d)That registers both the carousel-3d and the slide element globally, so no import is needed in any component of the app.
The script setup form shown earlier is the third option and the shortest, and it is the one the quick start uses. Note that the import names are Carousel3d and Slide while the template elements are carousel-3d and slide, which is the usual kebab-case resolution of a PascalCase component name.
The repository layout suggests the project is serious about the surrounding tooling. There is a separate types directory, a playwright config and a vitest config, a vite config plus a separate vite.umd.config.mjs for the UMD bundle, an eslint flat config at eslint.config.mjs, and a pnpm workspace file.
Guarding navigation with beforeSlideChange
A carousel that a user can swipe, dot or arrow their way out of a state is a problem when that state matters. The component offers a guard for exactly this.
The prop takes a function that receives the target index and returns whether the move is allowed:
<carousel-3d :before-slide-change="(index) => index !== 3">
<!-- slides -->
</carousel-3d>Returning false cancels the move, and the documentation is specific about the scope: the cancellation applies to arrows, swipes, dots and programmatic navigation. That last part is the one to note, because it means the guard is not bypassable by calling a method from code.
The event side is documented too. Returning false still lets the before-slide-change event fire for allowed moves, and the component emits before-slide-change, after-slide-change and last-slide. So a guard and a listener are separate concerns: one decides, the other observes.
Programmatic navigation goes through a template ref:
<carousel-3d ref="carousel">
<!-- slides -->
</carousel-3d>
<button type="button" @click="$refs.carousel.goSlide(3)">
Open slide 4
</button>The method list is goNext(), goPrev(), goSlide(index) and goFar(index), so jumping to an arbitrary slide and jumping to the far end are separate calls.
Lazy content and autoplay for image heavy slides
A 3D carousel is often a gallery of images, and rendering every slide at once is wasteful when the slides are far from the current one. The lazy prop addresses that, and the documentation describes it as rendering slide content only near the current slide:
<carousel-3d lazy :display="3" :count="items.length">
<slide v-for="(item, index) in items" :key="item.id" :index="index">
<img :src="item.image" :alt="item.title">
</slide>
</carousel-3d>The display prop sets how many slides are visible, and here it is three, which is also the number of slides that stay rendered around the current position.
Autoplay is controlled by autoplay, autoplayTimeout and autoplayHoverPause, the last of which pauses the rotation while the pointer is over the carousel. The feature list also mentions lazy slide content for image-heavy carousels as a distinct capability.
There is a plain warning in the documentation about using any of this. Carousels can hurt readability, so use autoplay sparingly and always provide a way to pause or navigate manually. When motion is a concern, the docs suggest a plain grid or a flat carousel with disable-3d. The clickable, disable3d and minSwipeDistance props are grouped as interaction and flat mode, and minSwipeDistance is the threshold that decides whether a drag counts as a swipe.
Accessibility, browser support and the maintenance picture
The accessibility story is built into the component rather than bolted on. Navigation works through pointer, touch, mouse and keyboard, and the controls are described as native, accessible buttons. The dots carry an aria-current state so a screen reader can tell which one is active, and the prev and next slots let you replace the arrow content while keeping the button semantics:
<carousel-3d dots controls-visible>
<template #prev>
<span aria-hidden="true">←</span>
</template>
<template #next>
<span aria-hidden="true">→</span>
</template>
<slide v-for="(item, index) in items" :key="item.id" :index="index">
{{ item.title }}
</slide>
</carousel-3d>The aria-hidden span is the detail that makes this correct: the arrow glyph is decorative because the button itself carries the meaning.
Browser support is stated as modern evergreen browsers, current Chrome, Firefox, Safari and Edge, and IE11 and older engines are not supported. The development toolchain requires Node 20.19+ or 22.13+ and pnpm 10.34.5, and the scripts cover lint, test, build, docs:build and pack:check.
The licence is MIT, and the last push was on 2026-09-10. Releases moved quickly in August 2026 with 1.2.0, 2.0.0 and 2.1.0 published on the same day, and the manifest version is now 2.1.1. The upgrade note matters for anyone still on Vue 2: the 2.x line supports Vue ^3.5.0, and existing Vue 2 users should remain on vue-carousel-3d@1. A CHANGELOG.md is at the repository root, and a separate README.zh-CN.md and localization plan show the project is being maintained in two languages.
Editorial conclusion
vue-carousel-3d fits a Vue 3 codebase that needs a stacked 3D presentation with touch and keyboard handling already solved, and where a flat grid would not read the way you want. It does not fit a content site where a rotating carousel would compete with the text, and the project itself advises using autoplay sparingly and offering disable-3d when motion matters. Before adopting it, check the Vue version in your app against the 2.x requirement of ^3.5.0, and confirm each slide carries a stable zero-based index and that dynamic collections pass the count prop.
Frequently asked questions
How do I install vue-carousel-3d?
With pnpm add vue-carousel-3d or npm install vue-carousel-3d. The 2.x line supports Vue ^3.5.0, and the documentation says existing Vue 2 users should remain on vue-carousel-3d@1.
How do I register the carousel in a Vue 3 project?
Locally with components: { Carousel3d, Slide } in a script setup block, or globally with createApp(App).use(Carousel3d), which registers both the carousel-3d and slide elements.
How do I stop the carousel from moving to a particular slide?
Use the beforeSlideChange prop and return false from it. Returning false cancels the move for arrows, swipes, dots and programmatic navigation, while the before-slide-change event still fires for allowed moves.
What props control the 3D look of vue-carousel-3d?
Perspective, inverseScaling and bias set the 3D depth and composition, while display, width, height and space control the visible slide count and the geometry.
Does vue-carousel-3d work with Vue 2?
Not in the 2.x line, which supports Vue ^3.5.0. The documentation says existing Vue 2 users should remain on vue-carousel-3d@1.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/wlada-vue-carousel-3d)Was this useful?