Skip to content

About

Offline spaced repetition flashcard app built with React Native, Expo Router and Reanimated. Import decks from plain JSON, study with an SM-2 style scheduler, and keep all progress on device. Fully unit tested TypeScript domain layer.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Flashcards

An offline flashcard app with spaced repetition, built with React Native and Expo. Author your cards as a plain JSON file (by hand or with an LLM), import them into the app, and study whatever is due each day. The app schedules reviews using an SM-2 style algorithm, entirely on your device. No server, no accounts, no sync.

Built as a portfolio project to showcase React Native and Expo development: file based routing, Reanimated animations, a pure TypeScript domain layer with full unit test coverage, and an import pipeline that never destroys study progress.

Screenshots

Home Question Answer Import and settings
Deck list with due counts and daily goal Study screen showing a question Flipped card showing the answer with feedback buttons Import, export and new cards per day settings

Features

  • 📥 JSON import as source of truth: pick a .json file with expo-document-picker; cards are validated row by row, and errors are reported per row instead of failing the whole file.
  • 🔀 Merge safe imports: cards are identified by a 64 bit hash of deck + question, so importing an updated file upserts content without ever destroying study progress. Imports never delete; a summary for each deck reports added / updated / unchanged / duplicates / errors.
  • 🧠 Spaced repetition (SM-2 lite): three feedback buttons (Again / Good / Easy) drive ease and interval updates, with a configurable daily cap on new cards and a 4am rollover hour so a late night session does not count as the next day.
  • 🎴 Animated card flip: the question shows first, then a tap flips the card with a Reanimated 4 animation. Lapsed cards requeue within the session, and a summary at the end shows the reviewed count, accuracy, and the Again / Good / Easy breakdown.
  • 📝 Markdown everywhere: questions and answers render Markdown, including fenced code blocks, so the app works well for technical topics (JavaScript, TypeScript, React Native, system design…).
  • 💾 Local persistence and backup export: all state lives in AsyncStorage; one tap exports a versioned JSON backup of your progress via expo-sharing.
  • 🌗 Light and dark theme: follows the system appearance.

Tech stack

Area Choice
Framework Expo SDK 57, React Native 0.86, React 19.2
Language TypeScript (strict)
Navigation Expo Router (file based, with typed routes)
Animation React Native Reanimated 4
State React Context + reducer (src/state/StoreProvider.tsx), persisted to AsyncStorage
Content rendering react-native-markdown-display
File I/O expo-document-picker, expo-file-system, expo-sharing
Testing Jest + jest-expo + React Native Testing Library

Architecture

The core design principle: the JSON file owns content; the app owns scheduling. All spaced repetition logic is isolated in a domain layer with no React or React Native imports, which keeps it trivially testable.

src/
├── app/           # Expo Router screens: Home (deck list), Study, Summary
├── components/    # FlipCard, FeedbackButtons, MarkdownText, themed primitives
├── domain/        # Pure TypeScript, no React/RN imports:
│   ├── identity.ts    #   card identity hashing (64 bit FNV-1a)
│   ├── validate.ts    #   import file validation with per row errors
│   ├── merge.ts       #   upsert merge of imported cards into stored state
│   ├── schedule.ts    #   SM-2 lite ease/interval/due date math
│   ├── session.ts     #   study session queue building and requeueing
│   └── stats.ts       #   session summary stats
├── state/         # StoreProvider (Context + reducer)
├── storage/       # AsyncStorage persistence + backup export
└── lib/           # dashboard/derived stats

Every domain module has a matching test suite under __tests__/ (13 suites covering identity hashing, merge semantics, scheduling math, session building, validation, and persistence).

Getting started

Prerequisites: Node.js 20+, and for simulators: Xcode (iOS) or Android Studio (Android).

  1. Install dependencies:

    npm install
  2. Start the dev server:

    npx expo start

    From the terminal UI you can open the app in an iOS simulator (i), Android emulator (a), or the web (w).

  3. Or build and run natively:

    npm run ios
    npm run android

Try it with the sample deck

The repo includes sample-deck.json. In the app, tap Import / Update, pick the file, and start studying. To make it available on a simulator, drag the file onto the simulator window (iOS) or push it with adb (Android).

Card file format

A flat JSON array with no IDs to manage, so it is easy to generate by hand or with an LLM:

[
  {
    "deck": "JavaScript",
    "question": "What is a closure?",
    "answer": "A function bundled together with references to its surrounding (lexical) scope."
  },
  {
    "deck": "JavaScript",
    "question": "What does this log?\n```js\nconsole.log(typeof null)\n```",
    "answer": "`\"object\"` — a long-standing bug kept for backwards compatibility."
  }
]

All three fields are required strings; question and answer support Markdown, including fenced code blocks.

Testing

npm test
npm run test:watch

Design docs

The design spec and implementation plan that guided the build live in docs/superpowers, including the reasoning behind the card identity hashing, the merge semantics on import, and the scheduling constants.

License

See LICENSE.

About

Offline spaced repetition flashcard app built with React Native, Expo Router and Reanimated. Import decks from plain JSON, study with an SM-2 style scheduler, and keep all progress on device. Fully unit tested TypeScript domain layer.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages