The Palos Publishing Company

Follow Us On The X Platform @PalosPublishing
Categories We Write About

Build a flashcard app for spaced repetition

Creating a flashcard app with spaced repetition involves building an interface for adding, reviewing, and scheduling cards based on user performance. Here’s a detailed breakdown and an example implementation using React for the frontend and a simple algorithm for spaced repetition.


Key Features of the Spaced Repetition Flashcard App

  1. Add Flashcards: User can create flashcards with a question and answer.

  2. Review Flashcards: Show cards due for review based on spaced repetition intervals.

  3. Spaced Repetition Algorithm: Schedule cards for review based on user’s recall performance.

  4. Storage: Save flashcards and progress (local storage for simplicity).


How Spaced Repetition Works (Simplified)

  • Each card has a due date indicating when it should be reviewed next.

  • When reviewing:

    • If the user recalls easily, increase the interval (e.g., 1 day → 3 days → 7 days → …).

    • If the user struggles, shorten the interval or reset.

  • This gradually spaces reviews further apart for well-known cards.


Basic Data Model for Each Flashcard

js
{ id: string, question: string, answer: string, interval: number, // days until next review dueDate: Date, easeFactor: number, // optional for advanced algorithms like SM-2 repetitions: number }

Example Implementation in React

jsx
import React, { useState, useEffect } from 'react'; // Helper to get today's date without time const today = () => new Date(new Date().setHours(0,0,0,0)); // Load from localStorage const loadCards = () => { const data = localStorage.getItem('flashcards'); return data ? JSON.parse(data).map(card => ({ ...card, dueDate: new Date(card.dueDate) })) : []; }; // Save to localStorage const saveCards = (cards) => { localStorage.setItem('flashcards', JSON.stringify(cards)); }; export default function FlashcardApp() { const [cards, setCards] = useState(loadCards()); const [question, setQuestion] = useState(''); const [answer, setAnswer] = useState(''); const [reviewingCard, setReviewingCard] = useState(null); const [showAnswer, setShowAnswer] = useState(false); // Cards due for review today or earlier const dueCards = cards.filter(card => card.dueDate <= today()); // Add a new card const addCard = () => { if (!question.trim() || !answer.trim()) return; const newCard = { id: Date.now().toString(), question, answer, interval: 1, // start with 1 day interval dueDate: today(), easeFactor: 2.5, // default ease factor for SM-2 algorithm repetitions: 0, }; const updatedCards = [...cards, newCard]; setCards(updatedCards); saveCards(updatedCards); setQuestion(''); setAnswer(''); }; // Update a card after review based on recall quality // quality: 5 (perfect) to 0 (forgot) const updateCardAfterReview = (card, quality) => { let { interval, easeFactor, repetitions } = card; if (quality >= 3) { if (repetitions === 0) interval = 1; else if (repetitions === 1) interval = 6; else interval = Math.round(interval * easeFactor); repetitions += 1; easeFactor = Math.max(1.3, easeFactor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))); } else { repetitions = 0; interval = 1; } const dueDate = new Date(); dueDate.setDate(dueDate.getDate() + interval); const updatedCard = { ...card, interval, easeFactor, repetitions, dueDate }; const updatedCards = cards.map(c => c.id === card.id ? updatedCard : c); setCards(updatedCards); saveCards(updatedCards); setReviewingCard(null); setShowAnswer(false); }; return ( <div style={{ maxWidth: 600, margin: 'auto', padding: 20, fontFamily: 'Arial, sans-serif' }}> <h2>Add Flashcard</h2> <input placeholder="Question" value={question} onChange={e => setQuestion(e.target.value)} style={{ width: '100%', padding: 8, marginBottom: 10 }} /> <textarea placeholder="Answer" value={answer} onChange={e => setAnswer(e.target.value)} style={{ width: '100%', padding: 8, marginBottom: 10, height: 60 }} /> <button onClick={addCard} style={{ padding: '8px 16px' }}>Add Card</button> <hr /> <h2>Review Flashcards</h2> {reviewingCard ? ( <div> <div style={{ marginBottom: 20 }}> <strong>Q:</strong> {reviewingCard.question} </div> {showAnswer ? ( <> <div style={{ marginBottom: 20 }}> <strong>A:</strong> {reviewingCard.answer} </div> <div> <button onClick={() => updateCardAfterReview(reviewingCard, 5)} style={{ marginRight: 5 }}>Easy</button> <button onClick={() => updateCardAfterReview(reviewingCard, 4)} style={{ marginRight: 5 }}>Good</button> <button onClick={() => updateCardAfterReview(reviewingCard, 3)} style={{ marginRight: 5 }}>Hard</button> <button onClick={() => updateCardAfterReview(reviewingCard, 2)} style={{ marginRight: 5 }}>Forgot</button> </div> </> ) : ( <button onClick={() => setShowAnswer(true)}>Show Answer</button> )} </div> ) : ( dueCards.length > 0 ? ( <button onClick={() => setReviewingCard(dueCards[0])}> Start Review ({dueCards.length} cards) </button> ) : ( <p>No cards due for review today. Come back later!</p> ) )} </div> ); }

Explanation

  • Adding cards: User inputs question and answer, stored with initial interval and due date today.

  • Reviewing cards: Only cards with dueDate <= today appear for review.

  • Spaced repetition: Uses a simplified version of the SM-2 algorithm (used by Anki) adjusting interval, ease factor, and repetitions based on recall quality.

  • Storage: Uses localStorage to persist cards and progress.

  • UI: Minimalistic with add and review functionality.


You can expand this by adding user accounts, syncing with a backend, more detailed statistics, or enhanced algorithms. This base provides a functional spaced repetition flashcard app in React.

Share this Page your favorite way: Click any app below to share.

Enter your email below to join The Palos Publishing Company Email List

We respect your email privacy

Categories We Write About