Examples
Basic usage
Source
vue
<script lang="ts" setup>
import { ref } from 'vue'
import { FlashCards } from '../src'
import './assets/index.css'
const cards = ref([
{ text: 'Front 1' },
{ text: 'Front 2' },
{ text: 'Front 3' },
])
</script>
<template>
<div class="w-full flex justify-center items-center py-20">
<div class="max-w-sm w-full">
<FlashCards :items="cards" #="{ item }">
<div class="p-5 bg-base-200 border border-base-300 shadow-lg rounded-lg h-40 flex justify-center items-center">
<div>{{ item.text }}</div>
</div>
</FlashCards>
</div>
</div>
</template>
With flip usage
Click on card to flip
Source
vue
<script lang="ts" setup>
import { ref } from 'vue'
import { FlashCards } from '../src'
import './assets/index.css'
const cards = ref([
{ text: 'Front 1', back: 'Back 1' },
{ text: 'Front 2', back: 'Back 2' },
{ text: 'Front 3', back: 'Back 3' },
])
</script>
<template>
<div class="w-full flex justify-center items-center py-20">
<div class="max-w-sm w-full">
<FlashCards :items="cards" flip>
<template #default="{ item }">
<div class="p-5 bg-base-200 border border-base-300 shadow-lg rounded-lg h-40 flex justify-center items-center">
<div>{{ item.text }}</div>
</div>
</template>
<template #back="{ item }">
<div class="p-5 bg-base-200 border border-base-300 shadow-lg rounded-lg h-40 flex justify-center items-center">
<div>{{ item.back }}</div>
</div>
</template>
</FlashCards>
</div>
</div>
</template>
With custom actions usage
Source
vue
<script lang="ts" setup>
import { ref } from 'vue'
import { FlashCards } from '../src'
import './assets/index.css'
const cards = ref([
{ text: 'Front 1' },
{ text: 'Front 2' },
{ text: 'Front 3' },
])
</script>
<template>
<div class="w-full flex justify-center items-center py-20">
<div class="max-w-sm w-full">
<FlashCards :items="cards">
<template #default="{ item }">
<div class="p-5 bg-base-200 border border-base-300 shadow-lg rounded-lg h-40 flex justify-center items-center">
<div>{{ item.text }}</div>
</div>
</template>
<template #actions="{ approve, reject, restore }">
<div class="grid grid-cols-2 gap-5 mt-5">
<div class="btn btn-error" @click="reject">
Reject
</div>
<div class="btn btn-primary" @click="approve">
Approve
</div>
<div class="col-span-2 btn" @click="restore">
Restore
</div>
</div>
</template>
</FlashCards>
</div>
</div>
</template>
Virtual rendering with many cards
Efficiently handles large datasets by only rendering visible cards
Source
vue
<script setup lang="ts">
import { ref } from 'vue'
import { FlashCards } from '../src'
// Generate 1000 cards for demonstration
const cards = ref(Array.from({ length: 1000 }, (_, i) => ({
id: i + 1,
text: `Card ${i + 1} of 1000`,
})))
const approved = ref<number[]>([])
const rejected = ref<number[]>([])
function onApprove(card: { id: number }) {
approved.value.push(card.id)
}
function onReject(card: { id: number }) {
rejected.value.push(card.id)
}
</script>
<template>
<div class="example-container">
<div class="cards-container">
<FlashCards
:items="cards"
:virtual-buffer="1"
#="{ item }"
@approve="onApprove"
@reject="onReject"
>
<div class="p-5 bg-base-200 border select-none border-base-300 shadow-lg rounded-lg h-40 flex flex-col gap-10 justify-center items-center">
<div class="card-text">
{{ item.text }}
</div>
<div class="card-stats">
<div>Approved: {{ approved.length }}</div>
<div>Rejected: {{ rejected.length }}</div>
<div>Remaining: {{ cards.length - approved.length - rejected.length }}</div>
</div>
</div>
</FlashCards>
</div>
</div>
</template>
<style scoped>
.example-container {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
.cards-container {
max-width: 400px;
width: 100%;
}
.card-content {
padding: 20px;
background-color: white;
color: black;
user-select: none;
border: 1px solid #e2e8f0;
border-radius: 8px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
height: 200px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.card-text {
font-size: 1.5rem;
text-align: center;
margin-bottom: 1rem;
}
.card-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
font-size: 0.875rem;
color: #64748b;
text-align: center;
}
</style>
Tinder-like cards
Swipeable cards with beautiful images and Tinder-like interactions. See how to use extra state for actions
Source
vue
<script setup lang="ts">
import { ref } from 'vue'
import { FlashCards } from '../src'
import './assets/index.css'
interface Card {
id: number
text: string
description: string
image: string
}
const items = ref<Card[]>([
{
id: 1,
text: 'Mountain Adventure',
description: 'Explore the peaks and valleys',
image: 'https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?w=800&q=80',
},
{
id: 2,
text: 'Beach Paradise',
description: 'Relax by the ocean',
image: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=800&q=80',
},
{
id: 3,
text: 'City Life',
description: 'Urban exploration',
image: 'https://images.unsplash.com/photo-1449824913935-59a10b8d2000?w=800&q=80',
},
{
id: 4,
text: 'Forest Retreat',
description: 'Connect with nature',
image: 'https://images.unsplash.com/photo-1511497584788-876760111969?w=800&q=80',
},
])
</script>
<template>
<div class="tinder-container">
<div class="tinder-cards">
<FlashCards
:items="items"
>
<template #default="{ item }">
<div class="tinder-card" :style="{ backgroundImage: `url(${item.image})` }">
<div class="card-content">
<h2>{{ item.text }}</h2>
<p>{{ item.description }}</p>
</div>
</div>
</template>
<template #empty>
<div class="tinder-empty">
No more cards!
</div>
</template>
<template #actions="{ approve, reject, restore, isEnd, canRestore }">
<div class="tinder-buttons">
<button class="return-button" :disabled="!canRestore" @click="restore">
↺
</button>
<button class="dislike-button" :disabled="isEnd" @click="reject">
✕
</button>
<button class="like-button" :disabled="isEnd" @click="approve">
♥
</button>
</div>
</template>
</FlashCards>
</div>
</div>
</template>
<style scoped>
.tinder-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
}
.tinder-cards {
position: relative;
margin-bottom: 20px;
}
.tinder-card {
width: 100%;
height: 500px;
background-size: cover;
background-position: center;
border-radius: 10px;
position: relative;
overflow: hidden;
}
.card-content {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 20px;
background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
color: white;
}
.card-content h2 {
margin: 0 0 10px;
font-size: 24px;
}
.card-content p {
margin: 0;
font-size: 16px;
}
.tinder-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
.tinder-buttons button {
width: 60px;
height: 60px;
border-radius: 50%;
border: none;
font-size: 24px;
cursor: pointer;
transition: transform 0.2s, opacity 0.2s;
}
.tinder-buttons button:disabled {
cursor: not-allowed;
opacity: 0.5;
transform: none;
}
.tinder-buttons button:not(:disabled):hover {
transform: scale(1.1);
}
.like-button {
background-color: #4CAF50;
color: white;
}
.dislike-button {
background-color: #f44336;
color: white;
}
.return-button {
background-color: #2196F3;
color: white;
}
.no-more-cards {
text-align: center;
font-size: 20px;
color: #666;
padding: 40px;
}
.swipe-left {
animation: swipeLeft 0.3s ease-out forwards;
}
.swipe-right {
animation: swipeRight 0.3s ease-out forwards;
}
@keyframes swipeLeft {
to {
transform: translateX(-200%) rotate(-20deg);
opacity: 0;
}
}
@keyframes swipeRight {
to {
transform: translateX(200%) rotate(20deg);
opacity: 0;
}
}
</style>
Word study with delta feedback
Language learning cards with visual feedback for known and unknown words. Shows how to use delta prop for smooth opacity transitions with custom approje/reject state.
Source
vue
<script lang="ts" setup>
import { ref } from 'vue'
import { FlashCards } from '../src'
import './assets/index.css'
interface WordCard {
word: string
translation: string
}
const cards = ref<WordCard[]>([
{ word: 'Hello', translation: 'Bonjour' },
{ word: 'World', translation: 'Monde' },
{ word: 'Thank you', translation: 'Merci' },
{ word: 'Goodbye', translation: 'Au revoir' },
{ word: 'Please', translation: 'S\'il vous plaît' },
])
</script>
<template>
<div class="w-full flex justify-center items-center py-20">
<div class="max-w-sm w-full">
<FlashCards
:items="cards"
>
<template #default="{ item }">
<div
class="p-5 bg-base-200 border border-base-300 shadow-lg rounded-lg h-40 flex flex-col justify-center items-center gap-2"
>
<div class="text-2xl font-bold">
{{ item.word }}
</div>
<div class="text-lg text-gray-600">
{{ item.translation }}
</div>
</div>
</template>
<template #approve="{ delta }">
<div
class="absolute bg-base-200 inset-0 flex items-center justify-center text-green-500 rounded-lg font-bold text-xl"
:style="{ opacity: delta }"
>
I know this!
</div>
</template>
<template #reject="{ delta }">
<div
class="absolute bg-base-200 inset-0 flex items-center justify-center text-red-500 rounded-lg font-bold text-xl"
:style="{ opacity: delta }"
>
Need to review
</div>
</template>
</FlashCards>
</div>
</div>
</template>