An elegant and interactive Vue 3 flip-card component with support for drag interactions, clicks, and hover effects across 4 rotation directions.
- 4 flip directions:
right,left,up,down - Multiple interactions: drag/swipe, click, hover
- Mobile & Desktop: compatible with touch and mouse
- Customizable: dimensions, styles, and transitions
- High performance: GPU-accelerated CSS 3D transforms
- Fully typed: complete TypeScript support
npm install @nv-dev/vue-flipcard
# or
yarn add @nv-dev/vue-flipcard
# or
pnpm add @nv-dev/vue-flipcardimport VFlipCard from '@nv-dev/vue-flipcard'Or register globally:
import { createApp } from 'vue'
import VFlipCard from '@nv-dev/vue-flipcard'
const app = createApp(App)
app.use(VFlipCard)
app.mount('#app')Add the module to your nuxt.config.ts:
export default defineNuxtConfig({
modules: [
'@nv-dev/vue-flipcard'
],
})The VFlipCard component will be automatically imported throughout your Nuxt app. No manual imports needed!
Since this is a client-only component, wrap it with <ClientOnly>:
<template>
<ClientOnly>
<VFlipCard flip-side="right" active-click>
<!-- CODE -->
</VFlipCard>
</ClientOnly>
</template><template>
<VFlipCard
width="200px"
height="150px"
flip-side="right"
active-click
active-drag
>
<template #front>
<p>Front side</p>
</template>
<template #back>
<p>Back side</p>
</template>
</VFlipCard>
</template>| Prop | Type | Default | Description |
|---|---|---|---|
width |
String | '200px' |
Card width |
height |
String | '150px' |
Card height |
flipSide |
'left' | 'right' | 'up' | 'down' |
'right' |
Flip direction |
activeClick |
Boolean | false |
Enable flip on click |
activeDrag |
Boolean | false |
Enable flip on drag/swipe (mobile only) |
activeHover |
Boolean | false |
Enable flip on hover (desktop only) |
Content displayed on the front face of the card.
Content displayed on the back face of the card.
This component is client-only for now and cannot be server-side rendered (SSR).
Always wrap the component with <ClientOnly>:
<template>
<ClientOnly>
<VFlipCard flip-side="right" active-click>
<template #front>Front content</template>
<template #back>Back content</template>
</VFlipCard>
</ClientOnly>
</template>This prevents hydration mismatches and ensures your component renders correctly.
<VFlipCard flip-side="right" active-click>
<template #front><img src="front.jpg" /></template>
<template #back><img src="back.jpg" /></template>
</VFlipCard><VFlipCard flip-side="up" active-hover>
<template #front>Hover me</template>
<template #back>Flipped!</template>
</VFlipCard><VFlipCard flip-side="left" active-drag width="300px" height="200px">
<template #front>Swipe left</template>
<template #back>Content</template>
</VFlipCard><VFlipCard
flip-side="down"
active-click
active-drag
active-hover
>
<template #front>Front</template>
<template #back>Back</template>
</VFlipCard>right: Drag right side towards you (clockwise, rotateY)left: Drag left side towards you (counter-clockwise, rotateY)up: Drag top towards you (upward, rotateX)down: Drag bottom towards you (downward, rotateX)
- Toggles between front and back
- Works on mobile and desktop
- Move your finger in the flip direction
- Drag distance is proportional to the card size (width for
left/right, height forup/down) - 90-degree threshold to determine final side
- Smooth animation with configurable transitions
- Desktop only (automatically detected)
- Shows back on hover, returns to front on leave
The component uses CSS classes for easy customization:
.flip-card-face {
border-radius: 6px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.front {
background: #fff;
}
.back {
background: #f7f7f7;
}Customize with scoped CSS:
<style scoped>
:deep(.flip-card-face) {
border: 2px solid #your-color;
}
</style>The component emits events when the card flips:
| Event | Payload | Description |
|---|---|---|
flip:front |
— | Emitted when the card flips to show the front face |
flip:back |
— | Emitted when the card flips to show the back face |
<script setup>
const onFlipToFront = () => {
console.log('Card flipped to front')
}
const onFlipToBack = () => {
console.log('Card flipped to back')
}
</script>
<template>
<VFlipCard
flip-side="right"
active-click
@flip:front="onFlipToFront"
@flip:back="onFlipToBack"
>
<template #front>Front</template>
<template #back>Back</template>
</VFlipCard>
</template>The component supports v-model binding for two-way synchronization of the card's flip state. This allows you to control the card programmatically from your Vue component.
true: Card is flipped to show the back facefalse: Card is showing the front face
<script setup>
import { ref } from 'vue'
import VFlipCard from '@nv-dev/vue-flipcard'
const isFlipped = ref(false)
</script>
<template>
<div>
<button @click="isFlipped = !isFlipped">
{{ isFlipped ? 'Show Front' : 'Show Back' }}
</button>
<VFlipCard
v-model="isFlipped"
flip-side="right"
active-click
active-drag
>
<template #front>Front side</template>
<template #back>Back side</template>
</VFlipCard>
</div>
</template>The component automatically updates the v-model value when:
- User clicks the card (with
activeClick) - User drags/swipes past 90 degrees (with
activeDrag) - User hovers (with
activeHoveron desktop)
You can also update the state programmatically:
const isFlipped = ref(false)
const flipCard = () => {
isFlipped.value = true
}
const resetCard = () => {
isFlipped.value = false
}<script setup>
import { ref } from 'vue'
const card1State = ref(false)
const card2State = ref(false)
const toggleAll = () => {
card1State.value = !card1State.value
card2State.value = !card2State.value
}
</script>
<template>
<div>
<button @click="toggleAll">Toggle all cards</button>
<VFlipCard v-model="card1State" flip-side="right">
<template #front>Card 1 Front</template>
<template #back>Card 1 Back</template>
</VFlipCard>
<VFlipCard v-model="card2State" flip-side="left">
<template #front>Card 2 Front</template>
<template #back>Card 2 Back</template>
</VFlipCard>
</div>
</template>- Chrome/Edge (latest versions)
- Firefox (latest versions)
- Safari (latest versions)
- Mobile iOS/Android with Touch Events support
- Uses CSS 3D transforms for GPU-accelerated animations
- Smooth CSS transitions (0.6s cubic-bezier by default)
- Minimal JavaScript computation
MIT
Nicolas VETROFF
https://github.com/nicolas-vetroff/vue-flipcard
This project is open to improvements and suggestions. Feel free to open issues.