Tinder-like flashcards component with dragging and flipping
npm install vue3-flashcardsA Tinder-like flashcards component for Vue 3 with smooth animations and intuitive gestures





📚 Documentation • 🎮 Examples • 🚀 Getting Started • ▶️ Interactive Demo
---
- 🎯 Tinder-style interactions - Intuitive swipe gestures with smooth animations
- 🔄 Card flipping - Two-sided cards with beautiful flip animations
- ⚡ Zero dependencies - Lightweight and performant, built purely with Vue 3 and CSS
- 🎨 Smooth animations - Hardware-accelerated CSS transitions for 60fps performance
- 🔧 Highly customizable - Extensive API with props, slots, events, and custom transforms
- 📱 Touch & Mouse support - Works seamlessly on desktop and mobile devices
- ♾️ Loop mode - Loop through cards endlessly for continuous swiping
- 🎯 Stack visualization - Show multiple cards stacked with customizable depth and direction
- ⚙️ Virtual rendering - Efficient rendering for large datasets with render limit
- 🔄 Restore functionality - Undo swipes and bring cards back to the stack
``bashnpm
npm install vue3-flashcards
🚀 Quick Start
$3
`vue
:items="cards"
#="{ item }"
>
{{ item.title }}
`$3
`vue
:items="cards"
:swipe-direction="['left', 'right', 'top']"
:resistance-effect="true"
:resistance-threshold="100"
:resistance-strength="0.5"
:swipe-threshold="150"
:stack="3"
:loop="true"
@swipe-left="onLeft"
@swipe-right="onRight"
@swipe-top="onTop"
>
{{ item.title }}
{{ item.description }}
❌ Nope
❤️ Like
⭐ Super Like
`$3
Install the plugin to register components globally and set default configuration:
`typescript
// main.ts
import { createApp } from 'vue'
import { FlashCardsPlugin } from 'vue3-flashcards'
import App from './App.vue'const app = createApp(App)
app.use(FlashCardsPlugin, {
flashCards: {
// Global defaults for FlashCards components
stack: 3,
stackOffset: 25,
swipeThreshold: 150,
loop: true,
},
flipCard: {
// Global defaults for FlipCard components
flipAxis: 'x',
waitAnimationEnd: false,
}
})
app.mount('#app')
`Now components are globally available without imports:
`vue
{{ item.title }}
Front
Back
`$3
For Nuxt applications, use the dedicated module:
`typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['vue3-flashcards/nuxt'], // Global configuration
flashcards: {
stack: 3,
stackOffset: 25,
swipeThreshold: 150,
loop: true,
}
})
`Components are auto-imported and globally available:
`vue
{{ item.title }}
Front
Back
`Features:
- ✅ SSR Compatible - Works perfectly with server-side rendering
- ✅ Auto-import - Components available without imports
- ✅ Global Config - Set defaults for all components
- ✅ TypeScript - Full IntelliSense in
nuxt.config.ts---
📖 API Reference
For complete documentation, visit documentation
$3
| Prop | Type | Default | Description |
|------|------|---------|-------------|
|
items | T[] | required | Array of items to display as cards |
| itemKey | string \| number | id | Property to track items by. When provided, items will be tracked by this property instead of their index. Should be unique for each item. This is recommended to use when you modify items array in runtime. |
| maxRotation | number | 20 | Maximum rotation angle in degrees |
| swipeThreshold | number | 150 | Swipe swipeThreshold in pixels |
| dragThreshold | number | 5 | Minimum drag distance to start swiping |
| swipeDirection | 'horizontal' \| 'vertical' \| ('left' \| 'right' \| 'top' \| 'bottom')[] | 'horizontal' | Direction of swiping: preset modes ('horizontal' for left/right, 'vertical' for up/down) or custom array of directions (e.g., ['left', 'right', 'top'] for Tinder-like UX). Affects swipe detection, default transform, and exit animations |
| maxDragY | number \| null | null | Maximum Y dragging distance in pixels (null = unlimited) |
| maxDragX | number \| null | null | Maximum X dragging distance in pixels (null = unlimited) |
| disableDrag | boolean | false | Completely disable dragging functionality. Manual methods and slot actions still work |
| resistanceEffect | boolean | false | Enable resistance when dragging beyond threshold |
| resistanceThreshold | number | 150 | Distance threshold for resistance effect to activate |
| resistanceStrength | number | 0.3 | Strength of resistance (0-1, where 1 is maximum resistance) |
| loop | boolean | false | Enable loop swiping mode (cards loop endlessly) |
| renderLimit | number | 3 | Cards to render. Can't be lower than 1. |
| stack | number | 0 | Number of cards to show stacked behind the active card. When stack is greater than renderLimit, renderLimit is automatically increased to stack + 2. |
| stackOffset | number | 20 | Offset in pixels between stacked cards. |
| stackScale | number | 0.05 | Scale reduction factor for stacked cards. Each card behind is scaled down by this amount × depth. |
| stackDirection | 'top' \| 'bottom' \| 'left' \| 'right' | 'bottom' | Direction where stacked cards appear relative to the active card. |
| waitAnimationEnd | boolean | false | Wait for animation to end before performing next action |
| transformStyle | (position: DragPosition) => string \| null | null | Custom transform function for card movement during drag |#### Transform Style Function
The
transformStyle prop allows you to customize how cards transform during drag interactions. It receives a DragPosition object with x, y, and delta properties.Default behavior:
`javascript
function defaultTransform(position) {
return transform: rotate(${position.delta * maxRotation}deg)
}
`Custom examples:
`javascript
// Scale effect
function scaleTransform(position) {
return transform: rotate(${position.delta 15}deg) scale(${1 - Math.abs(position.delta) 0.1})
}// Blur effect
function blurTransform(position) {
return
transform: rotate(${position.delta 20}deg); filter: blur(${Math.abs(position.delta) 3}px)
}
`$3
| Slot Name | Props | Description |
|-----------|-------|-------------|
| default |
{ item: T, activeItemKey: number \| string } | Main content of the card (front side) |
| actions | { restore: () => void, swipeTop: () => void, swipeLeft: () => void, swipeRight: () => void, swipeBottom: () => void, skip: () => void, reset: (options?) => void, isEnd: boolean, isStart: boolean, canRestore: boolean } | Custom actions UI. restore returns to previous card, directional methods trigger swipe animations, skip moves to next without approve/reject, reset resets all cards |
| top | { item: T, delta: number } | Content shown when swiping up (indicator) |
| left | { item: T, delta: number } | Content shown when swiping left (indicator) |
| right | { item: T, delta: number } | Content shown when swiping right (indicator) |
| bottom | { item: T, delta: number } | Content shown when swiping down (indicator) |
| approve | { item: T, delta: number } | ~~Content shown when swiping right~~ Deprecated: Use right or top slot instead |
| reject | { item: T, delta: number } | Deprecated: Use left or bottom slot instead |
| empty | - | Content shown when all cards have been swiped |Events
| Event Name | Payload | Description |
|------------|---------|-------------|
| swipeTop |
item: T | Emitted when a card is swiped up |
| swipeLeft | item: T | Emitted when a card is swiped left |
| swipeRight | item: T | Emitted when a card is swiped right |
| swipeBottom | item: T | Emitted when a card is swiped down |
| skip | item: T | Emitted when a card is skipped (skipped via actions) - moves to next card without swipe |
| restore | item: T | Emitted when a card is restored (returned to the stack via restore action) |
| loop | - | Emitted when a new loop cycle starts in loop mode (all cards have been swiped) |
| dragstart | item: T | Emitted when user starts dragging a card |
| dragmove | item: T, type: SwipeAction \| null, delta: number | Emitted during card dragging with movement details |
| dragend | item: T | Emitted when user stops dragging a card |
| approve | item: T | Deprecated: Use swipeRight or swipeTop event instead |
| reject | item: T | Deprecated: Use swipeLeft or swipeBottom event instead |Exposed
| Method/Property | Type | Description |
|----------------|------|-------------|
| swipeTop | () => void | Triggers upward swipe on current card |
| swipeLeft | () => void | Triggers left swipe on current card |
| swipeRight | () => void | Triggers right swipe on current card |
| swipeBottom | () => void | Triggers downward swipe on current card |
| restore | () => void | Returns to the previous card if available |
| skip | () => void | Triggers skip animation on current card - moves to next card without swipe |
| reset | (options?) => void | Resets all cards to initial state. Options: { animated?: boolean, delay?: number } |
| canRestore | boolean | Whether there is a previous card to restore to |
| isEnd | boolean | Whether all cards have been swiped |
| approve | () => void | Deprecated: Use swipeRight() or swipeTop() instead |
| reject | () => void | Deprecated: Use swipeLeft() or swipeBottom() instead |FlipCard Component
The
FlipCard component provides card flipping functionality and can be used independently or within FlashCards.$3
| Prop Name | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| disabled |
boolean | No | false | Disable card flipping functionality |
| waitAnimationEnd | boolean | No | true | Wait for animation to end before allowing another flip |
| flipAxis | 'x' \| 'y' | No | 'y' | Axis of rotation for the flip animation (x = horizontal, y = vertical) |$3
| Slot Name | Props | Description |
|-----------|-------|-------------|
| front |
{ flip: () => void } | Content shown on the front of the card. Receives flip method for programmatic flipping |
| back | { flip: () => void } | Content shown on the back of the card (optional). Receives flip method for programmatic flipping |$3
| Event Name | Payload | Description |
|------------|---------|-------------|
| flip |
isFlipped: boolean | Emitted when the card is flipped. true when showing back side, false when showing front side |$3
| Method | Type | Description |
|--------|------|-------------|
| flip |
() => void | Programmatically flip the card. Respects disabled and waitAnimationEnd props |$3
`vue
Front Content
Back Content
``