A KJV Bible memorization app that runs entirely in the browser on GitHub Pages at gmlewis.github.io/kjv-ref.
- No ads. No tracking. No data collection. No analytics.
- No accounts, no login, no cookies, no third-party services.
- All your data stays in your browser's
localStorage— it never leaves your device. - Fully open source — inspect every line of code.
- 7 practice modes: Word Bank, First Letters, Simplified Vanishing Cloze, Vanishing Cloze, Multiple Choice, Reference Match, Full Recall
- Multi-verse ranges: bookmark and practice verse ranges like "Psalms 23:1-6"
- Spaced repetition: SM-2 algorithm schedules reviews at increasing intervals
- Strong's Concordance: tap any word for Hebrew/Greek lexicon data
- Interlinear text: side-by-side original language with English, with clickable word popovers
- Full-text search across all 24,857 KJV verses with advanced syntax (phrases, wildcards, OR, exclude)
- Fuzzy Bible reference search: type
jn3:16,ps23, or1 john 2to jump to any verse - Bookmarks — save individual verses or verse ranges for focused practice
- Settings export/import — download all your data as JSON, share favorites with others
- Daily goals — track your memorization streak
- Achievements — earn badges for milestones
- Dark mode — toggle in the nav bar or with the
tkey - Keyboard shortcuts —
?for help,/for search,←/→for verse navigation, and more - Verse sharing — copy direct links to any verse or verse range
- Zero dependencies on external services — all data is static JSON served from GitHub Pages
- Runtime/PM: Bun 1.3+
- Framework: React 18 + TypeScript
- Build: Vite 5
- Styling: Tailwind CSS 3
- Routing: react-router-dom 7
- Tests: Vitest (unit), Playwright (e2e)
- Deploy: GitHub Pages (auto-deploy on push to
master)
Install Bun (one-time):
curl -fsSL https://bun.sh | bashThen verify:
bun --version # should print 1.3.x or newerFor e2e tests, also install Playwright's browser (one-time):
bunx playwright install chromiumbun installThat's it. No SDK to install, no platform account, no typegen step.
bun run devThe app renders fully in the local dev server — no iframe, no blank screen.
Open the URL Vite prints (typically http://localhost:3000).
The Vite
baseis/kjv-ref/to match the GitHub Pages project path. In dev this is handled automatically byimport.meta.env.BASE_URL, which theBrowserRouteruses as itsbasename.
bun run build # tsc + vite build -> ./dist
bun run preview # serve ./dist locallyThe preview server serves at http://localhost:4173/kjv-ref/ — the same
subpath the live site uses. This is the best way to verify the production
bundle before pushing.
Uses Vitest with jsdom.
bun run test # watch mode (re-runs on file changes)
bun run test:run # single run
bun run test:coverage # with coverage report| Test file | What it covers |
|---|---|
src/data/kjv-bible.test.ts |
KJV parser — all 66 book abbreviations, verse lookup, chapter listing |
src/data/kjv-search.test.ts |
Full-text verse search (MiniSearch) |
src/data/strongs.test.ts |
Strong's lexicon lookup |
src/data/strongs-index.test.ts |
Strong's word index |
src/data/interlinear.test.ts |
Interlinear word mapping |
src/utils/practiceHelpers.test.ts |
Word Bank shuffle, First Letters, Vanishing Cloze, cloze mask, first-letter helpers |
src/components/Practice.cloze.test.ts |
Vanishing Cloze diff/score, cloze mask, first-letter helpers |
src/components/Practice.simplified-cloze.test.ts |
Simplified Vanishing Cloze first-letter validation logic |
src/components/Practice.range.test.ts |
Multi-verse range practice sessions |
src/components/Practice.distractors.test.ts |
Multiple-choice / reference-mode distractor selection |
src/components/Practice.verse-count.test.ts |
Practice verse filtering |
src/utils/spacedRepetition.test.ts |
SM-2 review scheduling |
src/utils/bibleBooks.test.ts |
Book metadata, prev/next chapter, verse navigation |
src/utils/verseNav.test.ts |
Verse-level next/prev navigation with wraparound |
src/utils/bookmarkHelpers.test.ts |
Bookmark logic |
src/utils/studyLinks.test.ts |
BibleHub / STEP Bible links |
src/utils/urlHelpers.test.ts |
URL helpers, verse hash ranges |
src/utils/verseScroll.test.ts |
Verse hash scroll target parsing |
src/utils/bibleQueryParser.test.ts |
Advanced search expression parser (phrases, wildcards, OR, exclude) |
src/utils/bibleQueryEval.test.ts |
Bible query evaluator against real KJV data |
src/utils/bibleRefSearch.test.ts |
Fuzzy Bible reference search (abbreviations, ranges) |
src/utils/settingsTransfer.test.ts |
Settings export/import (bookmarks, round-trip, group bookmarks) |
src/components/StrongsPopover.test.tsx |
Strong's popover component |
src/components/InterlinearWordPopover.test.tsx |
Interlinear word popover, RTL positioning fix |
src/components/KeyboardModals.test.tsx |
Shortcuts modal, search modal |
src/components/Books.keyboard.test.tsx |
Keyboard shortcuts for verse/chapter navigation |
scripts/build-interlinear-words.test.ts |
Interlinear build script |
Uses Playwright. The config auto-builds the app and
starts a vite preview server, so a single command runs the full pipeline:
bun run e2e # headless (auto-builds + serves ./dist)
bun run e2e:headed # watch the browser
bun run e2e:ui # interactive picker
bun run e2e:report # open the last HTML reportRun a single spec or test:
bunx playwright test e2e/navigation.spec.ts --headed
bunx playwright test --grep "Word Bank" --headedNo auth setup needed. The static site is fully public — there is no Google OAuth, no cookie extraction, no
e2e/.auth/state.json.
| Spec file | What it covers |
|---|---|
e2e/navigation.spec.ts |
Nav bar, all 5 sections, mobile hamburger menu |
e2e/dashboard.spec.ts |
Hero, stat cards, featured verses, CTAs |
e2e/practice.spec.ts |
Mode selector, Word Bank, First Letters, Simplified Vanishing Cloze, Vanishing Cloze, Multiple Choice, Reference Match, session summary, progress tracking |
e2e/books.spec.ts |
Books grid, Old/New filter, search, book detail, chapter view, featured verses, chapter jump navigation |
e2e/stats-achievements.spec.ts |
Statistics page, Achievements page |
Run this sequence before pushing to master (which triggers the live deploy):
bun install # clean install
bun run test:run # unit tests
bun run build # production build
bun run e2e # e2e tests against the built bundleIf all four pass, the GitHub Pages deploy will succeed — the CI workflow runs
the same bun install + bun run build.
Deployment is automatic. Pushing to master triggers
.github/workflows/deploy.yml, which runs bun install + bun run build and
pushes ./dist to the gh-pages branch (via peaceiris/actions-gh-pages).
GitHub Pages is configured to publish from the gh-pages branch, so that push
triggers the actual pages build and deployment build. The site goes live at
https://gmlewis.github.io/kjv-ref/.
There is no manual deploy step.
- Repository → Settings → Pages: set "Build and deployment" source to
Deploy from a branch, branch
gh-pages//(root). Thedeploy.ymlworkflow builds the app and pushes./distto that branch; GitHub Pages publishes from it. - The default branch must be
master.
Occasionally the pages build and deployment step gets wedged in GitHub's
build queue: the Deploy to GitHub Pages workflow succeeds and the gh-pages
branch is updated, but no pages build and deployment run ever appears in
the Actions list and the live site stays on the old version. GitHub shows no
outage when this happens — it's a per-repo queue stall.
The GitHub Pages build API is the source of truth (the Actions run is only created once the build actually starts, so a stuck build shows nothing there):
# Check the latest Pages build. duration:0 with a stale created_at = stuck
# (a healthy build runs in ~20-30s and reports a non-zero duration).
gh api repos/gmlewis/kjv-ref/pages/builds/latest --jq '{status, duration, created_at, commit}'
# Overall Pages status (prints "building" while stuck, "built" when healthy)
gh api repos/gmlewis/kjv-ref/pages --jq .status
# Re-trigger the Pages build (enqueues a fresh build; the stuck one is abandoned)
gh api -X POST repos/gmlewis/kjv-ref/pages/buildsAfter re-triggering, poll the latest build until status is built (or
errored). A fresh build typically completes in ~20-30s, at which point the
pages build and deployment run appears in the Actions list and the site is
live. This is a GitHub-side queue stall — the deploy workflow and gh-pages
push both succeeded, so no code change is needed.
Deep links (e.g. /kjv-ref/books/Genesis/1) work on refresh thanks to the
public/404.html SPA fallback, which redirects back into the app and restores
the intended path via history.replaceState in index.html.
| Mode | Description | When it appears |
|---|---|---|
| Word Bank | Tap chips to assemble the verse | Always available |
| First Letters | Each word is shown as its first letter only | Always available |
| Simplified Vanishing Cloze | Like Vanishing Cloze, but you only type the first letter of each blanked word — easier adaptive practice | Always available |
| Vanishing Cloze | Words are progressively blanked out (0% → 25% → 50% → 75% → 100%) | Always available; blanking level scales with timesRecited |
| Multiple Choice | Pick the correct verse text from 4 options | Always available |
| Reference Match | Given the text, recall the reference | Always available |
| Full Recall | Type the verse from memory | Hidden behind "Show all modes" |
What does "cloze" mean? A cloze test is a classic language-learning exercise where some words are removed from a text and you have to fill them in from context (the name comes from "closure" — your brain closes the gaps). "Vanishing" refers to the fact that more words vanish (get blanked out) as you master the verse. The Simplified variant asks only for the first letter of each blanked word, making it a lighter-weight stepping stone to the full Vanishing Cloze mode.
Spaced repetition (due reviews) are surfaced at the top of the practice queue and shown as a badge in the nav.
All user data lives in localStorage under the kjv-* keys:
kjv-theme— dark or light theme preferencekjv-verse-font-size— verse display font size (adjustable with+/-)kjv-strongs-enabled— whether Strong's numbers are shownkjv-interlinear-enabled— whether interlinear text is shownkjv-memorize-progress— per-verse:timesRecited,status,streak,accuracykjv-memorize-sessions— each practice session: score, mode, verseskjv-memorize-achievements— earned badgeskjv-memorize-bookmarks— saved verses and verse ranges (e.g. "Psalms 23:1-6")kjv-memorize-daily-goal— daily target and completionkjv-memorize-review-schedule— SM-2 spaced repetition schedule
Settings can be exported to a JSON file and imported on another browser via the download/upload buttons in the nav bar.
Cross-tab sync uses the browser storage event. The layer lives in
src/storage.ts; the React hooks that wrap it are in src/hooks.ts.
Reference data (the KJV text, Strong's lexicons, interlinear word mappings) is
served as static JSON from public/ and fetched on demand — no file storage
service involved.
kjv-ref/
├── src/
│ ├── components/ # React UI components
│ │ ├── Practice.tsx # All practice modes (single-verse + multi-verse ranges)
│ │ ├── Books.tsx # Browse / chapter view / search panel / verse ranges
│ │ ├── Dashboard.tsx
│ │ ├── Statistics.tsx
│ │ ├── Achievements.tsx
│ │ ├── Navigation.tsx # Nav bar + global keyboard shortcuts + settings export/import
│ │ ├── KeyboardModals.tsx # ? shortcuts modal + / search modal
│ │ ├── StrongsPopover.tsx
│ │ └── InterlinearWordPopover.tsx
│ ├── data/
│ │ ├── kjv-bible.ts # KJV parser + lazy loader
│ │ ├── kjv-verses.ts # Flat verse list (curated ~41 verses)
│ │ ├── kjv-search.ts # MiniSearch full-text index
│ │ ├── strongs.ts # Strong's lexicon lookup
│ │ ├── interlinear.ts # Interlinear word mapping
│ │ └── dataUrls.ts # Static URL registry (no-op for static site)
│ ├── utils/
│ │ ├── practiceHelpers.ts # Word Bank, First Letters, Vanishing Cloze, Simplified Vanishing Cloze
│ │ ├── spacedRepetition.ts # SM-2 algorithm
│ │ ├── bibleBooks.ts # Book metadata, prev/next chapter, verse navigation
│ │ ├── bibleRefSearch.ts # Fuzzy Bible reference search (abbreviations, ranges)
│ │ ├── bibleQueryParser.ts # Advanced search expression parser (phrases, wildcards)
│ │ ├── bibleQueryEval.ts # Bible query evaluator against KJV text
│ │ ├── settingsTransfer.ts # Settings export/import (download/upload JSON)
│ │ ├── bookmarkHelpers.ts
│ │ ├── studyLinks.ts # BibleHub / STEP Bible links
│ │ └── urlHelpers.ts # URL helpers, verse hash ranges
│ ├── hooks.ts # localStorage-backed React hooks
│ ├── storage.ts # localStorage data layer
│ ├── App.tsx # Router + routes
│ └── main.tsx # React root
├── public/
│ ├── kjv.txt # Full KJV Bible text (4.2 MB)
│ ├── interlinear/ # Hebrew/Greek interlinear (33 MB)
│ ├── strongs/ # Strong's lexicons (7.2 MB)
│ └── 404.html # SPA fallback for deep links
├── e2e/ # Playwright specs
│ ├── helpers/app-frame.ts
│ ├── navigation.spec.ts
│ ├── dashboard.spec.ts
│ ├── practice.spec.ts
│ ├── books.spec.ts
│ └── stats-achievements.spec.ts
├── scripts/ # Data-build scripts (interlinear, strongs index)
├── .github/workflows/
│ ├── ci.yml # on-push-or-pr: build + test + e2e
│ └── deploy.yml # GitHub Pages deploy
├── run.sh # Dev server + auto-open Chrome
├── vite.config.ts
├── playwright.config.ts
├── tsconfig.json
└── package.json
| Script | Description |
|---|---|
bun run dev |
Start Vite dev server |
bun run build |
TypeScript compile + Vite production build |
bun run preview |
Serve the production build locally |
bun run test |
Unit tests in watch mode |
bun run test:run |
Unit tests, single run |
bun run test:coverage |
Unit tests with coverage report |
bun run e2e |
Playwright e2e tests (auto-builds + serves) |
bun run e2e:headed |
Playwright e2e tests, headed |
bun run e2e:ui |
Playwright interactive UI |
bun run e2e:report |
Open last HTML e2e report |
bun run parse |
Re-parse kjv.txt into verse data |
Press ? anywhere in the app to see the full list. Key shortcuts:
| Key | Action |
|---|---|
? |
Show keyboard shortcuts help |
/ |
Open full-Bible search dialog (with fuzzy reference jump) |
→ / ← |
Next / previous verse (wraps across chapters and books) |
Shift+→ / Shift+← |
Jump to last verse / verse 1 of current chapter |
⌘→ / ⌘← |
Next / previous chapter |
g |
Go to the book list |
t |
Toggle dark / light theme |
+ / − |
Increase / decrease verse text size |
Home / End |
Scroll to top / bottom of page |
Esc |
Close any dialog or popover |
This is a fully static KJV Bible memorization app. All user data lives in
localStorage, all reference data is served as static JSON from public/,
and the site deploys automatically to GitHub Pages on push to master.