A customizable Pokédex generator and tracker. Build a personal dex for any mainline Pokémon game (Red/Blue through Legends Z-A + Pokémon HOME), configure 15+ filter options, mark every catch, track shiny hunts, and sync across devices.
11 languages. Offline-first. Installable as a PWA.
No live demo. Due to German legal requirements (Impressumspflicht, DSGVO), the original author does not operate a public instance. This software is provided as source code for self-hosting only. If third-party instances exist, they are operated independently and at their own legal responsibility.
Dex building
- Custom dex for every mainline game from Red/Blue through Legends Z-A, plus Pokémon HOME
- 15+ toggleable filters: regional forms, DLC regions, shinies, Mega/Gigantamax, gender forms, and sub-dex groupings (Paldea + Kitakami + Blueberry, Isle of Armor + Crown Tundra, etc.)
- 5-step creator wizard that generates the correct boxes, sort order, and form variants for the chosen game
- Duplicate an existing dex, or start from scratch with a fresh config
Tracking
- Per-entry catch state with one-click polaroid pop / unpop
- Per-entry metadata: gender, original-trainer flag, "caught in correct ball" flag, Pokémon HOME region, Pokémon GO origin, and a custom flag
- Shiny hunt tracker with configurable method (full-odds, Masuda, chain, outbreak, …), cumulative-odds math, and per-encounter counting
- Box renaming, per-box progress, dex rename, delete with undo
Stats & comparison
- Per-dex completion % and per-box progress with hand-drawn (rough.js) progress bars
- Dex-compare view across multiple games (overlap, missing, exclusive per game)
- Achievements system with unlock toasts
Cross-device
- Offline-first: every action works without internet; IndexedDB is the local source of truth
- Lamport-clock op log + outbox → SSE pipeline for realtime sync across devices
- Guest mode (no account) with one-click reconciliation on login
- Full data export (JSON per dex) for GDPR Art. 20 portability
- Account deletion (GDPR Art. 17) that cascades everything
UX
- 11 languages: English, German, Spanish, French, Italian, Japanese, Korean, Simplified & Traditional Chinese, Hindi, Thai
- Responsive layout with mobile drawer navigation
- Light / dark / system theme + adjustable UI scale
- Fuzzy search that finds "pika" → Pikachu, Pichu, Raichu
- Keyboard shortcuts and a context-menu for power users
- Dex sharing via signed public links
Installation
- PWA: install to home screen on iOS / Android / desktop
- Service-worker caching of the app shell + 1.8 MB tileset for offline rendering
- OAuth login via Google or Discord (optional), or email + password
The app ships with 11 languages. English and German have been reviewed and are considered correct. The remaining 9 languages (Spanish, French, Italian, Japanese, Korean, Simplified Chinese, Traditional Chinese, Hindi, Thai) were generated by AI and have not yet been fully verified — errors are likely. The Pokémon data layer (1 025+ species across 20 games) was hand-curated but may contain inaccuracies for edge cases (rare forms, event-only variants, regional availability).
If you find a mistake:
- Translation error (wrong word, broken grammar, missing key) — open an issue with the locale code, the key path (e.g.
DexView.contextMenu.flagHome), and the correction. - Dex data error (wrong Pokémon in a game's dex, missing form, incorrect shiny availability) — open an issue with the game, the Pokémon, and what's wrong.
- UI bug — open an issue with a screenshot and the steps to reproduce.
All issues are welcome at the project's GitHub Issues page.
The project is in active polish, not a finished product. Contributions — or just bug reports — on any of the following are especially welcome.
Translation review — native-speaker proofreading wanted. Each of the 9 AI-generated locales needs a pass for grammar, idioms, and Pokémon-community terminology:
- Spanish (
es) - French (
fr) - Italian (
it) - Japanese (
ja) - Korean (
ko) - Simplified Chinese (
zh-Hans) - Traditional Chinese (
zh-Hant) - Hindi (
hi) - Thai (
th)
Cross-device testing — the app has mainly been exercised on desktop Chrome. Reports on any of the following help a lot:
- Display on narrow mobile viewports (iPhone SE, small Android)
- Display on tablet widths (iPad portrait / landscape)
- Display on ultra-wide monitors
- Touch interactions on mobile (polaroid pop, sidebar drawer, context menus)
- Performance on low-end Android devices (rough.js paints, store rerenders)
- Performance on slow networks (offline PWA shell, SSE reconnect behaviour)
- iOS Safari PWA install flow + service worker lifecycle
- Firefox & Safari rendering parity with Chromium
Other open work:
- Human-designed logo — the current sun-and-book shield was generated with AI assistance; a hand-crafted mark matching the paper/polaroid aesthetic would be a better fit.
- Sketch-style charts — admin and stats pages use Recharts in its default clean style; they should match the app's hand-drawn visual language (rough.js fill, wobbly axes).
- Per-route browser tab titles — most pages fall back to the generic "Monarium" title; each route should set a translated
<title>visible in the browser tab.
This project was rebuilt from a hand-coded prototype (a Vite + Mantine app) with the help of generative AI (Claude, Anthropic).
What comes from the original author:
- The dex generation algorithm (
src/lib/generator/Generator.ts) — the core logic that turns a game + config into a set of 30-slot boxes with the correct Pokémon, forms, regional variants, shinies, and sub-dex groupings. Ported verbatim; covered by snapshot tests. - The complete Pokémon data layer (
src/lib/pokemon/DataCollections.ts,availablePokemon/*,gameConfigs.ts) — 1 025+ Pokémon across 20 games, hand-curated over years. - The visual design language — paper/wood textures, polaroid cards, tape effects, per-game colour palettes, handwritten headings. Concept and assets from the original app.
- Feature design, UX decisions, and product direction throughout.
What was generated / co-authored with AI:
- The Next.js App Router architecture, component structure, and server/client split.
- The Postgres schema, Drizzle ORM integration, and sync pipeline (Lamport clocks, outbox, SSE).
- All shadcn/ui component wiring, Tailwind styling, and responsive layout code.
- The authentication system (session management, email verification, password reset).
- Translations for 10 non-English locales (AI-generated, then human-reviewed and corrected).
- Test infrastructure (Vitest unit tests, Playwright E2E tests).
- This README.
The generator algorithm and data layer are the intellectual core of the project. The application shell around them is the product of human direction + AI implementation.
This software is designed to be self-hosted. If you deploy it publicly, you are the data controller under GDPR / DSGVO and must:
- Fill in the imprint — edit
Imprint.values.*insrc/messages/{locale}.jsonwith your name, address, and contact information (required by law in the EU). - Review the privacy policy — the template at
/privacydescribes the app's actual data processing. Adjust the "Third-party processors" section to list your hosting provider, database provider, and email service. - Comply with GDPR — the app provides account deletion (Art. 17) and full data export (Art. 20) out of the box. Ensure your hosting setup meets the remaining obligations (e.g. data processing agreements with your providers).
The built-in imprint and privacy policy pages are templates, not legal advice.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Node runtime) |
| UI | shadcn/ui (base-nova) + Tailwind CSS v4 |
| Database | PostgreSQL 16 + Drizzle ORM |
| Auth | Hand-rolled sessions (argon2id hashing, httpOnly cookies) |
| Sync | Lamport-clock op log + IndexedDB outbox + SSE (pg LISTEN/NOTIFY) |
| i18n | next-intl (11 languages) |
| PWA | Serwist service worker + Web App Manifest |
| Charts | Recharts |
| Testing | Vitest (unit) + Playwright (E2E) |
| Fonts | Caveat (headings), Geist Sans (body), CJK/Indic/Thai handwriting fallbacks |
- Node.js 20+
- pnpm 9+
- Docker (for Postgres + MailHog)
git clone <repo-url>
cd monarium
pnpm installdocker compose up -dThis starts:
- Postgres 16 on port
5433(not 5432 to avoid conflicts with a local Postgres) - MailHog on port
1025(SMTP) and8025(web UI for viewing verification emails)
cp .env.example .env.localThe defaults work for local development:
DATABASE_URL=postgres://postgres:postgres@localhost:5433/monarium
DATABASE_URL_DIRECT=postgres://postgres:postgres@localhost:5433/monarium
AUTH_SECRET=<generate with: openssl rand -base64 32>
NEXTAUTH_URL=http://localhost:3000
MAIL_FROM=Monarium <noreply@localhost>pnpm db:push # Sync schema to Postgres
pnpm db:seed # Seed default achievementspnpm devOpen http://localhost:3000. Register an account — the verification email appears in MailHog at http://localhost:8025.
There is no self-service admin promotion. Use the CLI script:
# Promote a verified user to admin
pnpm admin:promote you@example.com
# Demote back to regular user
pnpm admin:promote you@example.com --demoteThe script:
- Requires the user to already exist and have a verified email
- Is idempotent (re-running on an admin is a no-op)
- Uses
DATABASE_URLfrom.env.local— same privilege as direct DB access
Admin users see an /admin page with usage statistics and achievement management (CRUD).
The app runs on any platform that can host a Node.js 20+ server and connect to a PostgreSQL 16+ database. There is no vendor lock-in.
- Node.js 20+ with the Node runtime (not Edge) — needed for the native argon2 binding,
pgLISTEN/NOTIFY, and httpOnly session cookies. - PostgreSQL 16+ — the database stores users, dex data, achievements, and the sync event log.
- Two database connections — if you use a connection pooler (e.g. PgBouncer), you need a second direct (non-pooled) connection string. The SSE endpoint (
/api/dexes/[id]/stream) runsLISTENwhich poolers do not forward. Set both:DATABASE_URL— pooled, used for normal queriesDATABASE_URL_DIRECT— direct, used by the SSE subscriber- If you don't use a pooler, both can point to the same database.
- SMTP service — for sending verification and password-reset emails. Any SMTP-capable provider works (Resend, Mailgun, AWS SES, self-hosted Postfix, etc.).
# Required
DATABASE_URL=postgres://user:pass@host:5432/monarium
DATABASE_URL_DIRECT=postgres://user:pass@host:5432/monarium
AUTH_SECRET=<generate with: openssl rand -base64 32>
NEXTAUTH_URL=https://your-domain.com
# SMTP (any provider with SMTP credentials)
SMTP_HOST=smtp.your-provider.com
SMTP_PORT=587
SMTP_SECURE=false # true only for port 465
SMTP_USER=...
SMTP_PASS=...
MAIL_FROM=Monarium <noreply@your-domain.com>
# Optional — OAuth login providers
AUTH_GOOGLE_ID=...
AUTH_GOOGLE_SECRET=...
AUTH_DISCORD_ID=...
AUTH_DISCORD_SECRET=...-
Build the app:
pnpm install pnpm build # Uses webpack (required for service worker) -
Set up the database:
pnpm db:migrate # Apply the initial schema migration pnpm db:seed # Seed default achievements
-
Start the server:
pnpm start # Starts on port 3000 by default # or: PORT=8080 pnpm start
-
Register + promote an admin — see the section below.
-
Fill in legal pages — edit
Imprint.values.*and reviewPrivacy.sections.*in the locale files undersrc/messages/. See the self-hosting disclaimer above.
- The build command is
next build --webpackbecause the Serwist service worker plugin requires webpack. Development uses Turbopack (the defaultpnpm dev). - The SSE endpoint holds one PostgreSQL connection per open browser tab. This is fine for personal or small-community use. If you need to scale beyond ~100 concurrent tabs, swap
pg LISTENfor a Redis pub/sub layer — the app code change is isolated tosrc/app/api/dexes/[id]/stream/route.ts. - The service worker (
public/sw.js) caches the app shell and tileset for offline use. It is compiled at build time — no runtime compilation needed. - Static assets (tileset, textures, fonts, icons) are served from
/public/and cached aggressively by the service worker.
The app displays Pokémon as a single sprite sheet (public/tileset.webp) instead of loading individual images. The tileset and its coordinate mapping are checked into the repo, but if you need to regenerate them (e.g. when a new game adds Pokémon), run:
pnpm tileset:createThis downloads ~3 000 individual sprites from PokéAPI's sprite repo, composites them into a 96×96 tile grid (30 tiles per row), converts to WebP, and writes:
public/tileset.webp— the sprite sheet served to users (~1.8 MB)public/tileset-mapping.json—{ "key": { "x": col, "y": row } }lookup
The script takes 5–15 minutes depending on network speed. It is idempotent — re-running overwrites the output.
When to regenerate:
- A new Pokémon game is added to
DataCollections.tswith new species or forms - The
ImageMappingstable inscripts/create-tileset.tsis updated with new form→sprite-ID mappings - PokéAPI updates their sprite set (e.g. higher-resolution art)
How the app uses it:
The SpriteTile component reads tileset-mapping.json at import time and renders each Pokémon via background-image: url(/tileset.webp) + background-position computed from the tile's (x, y) coordinates. The grayscale() CSS filter dims uncaught entries — no second sprite set needed.
| Script | Description |
|---|---|
pnpm dev |
Start dev server (Turbopack) |
pnpm build |
Production build (webpack, for Serwist) |
pnpm start |
Start production server |
pnpm test |
Run Vitest unit tests |
pnpm e2e |
Run Playwright E2E tests |
pnpm typecheck |
TypeScript type check |
pnpm analyze |
Bundle analyzer (opens treemap) |
pnpm db:push |
Sync schema to database (dev) |
pnpm db:migrate |
Apply SQL migrations (production) |
pnpm db:seed |
Seed default achievements |
pnpm db:studio |
Open Drizzle Studio (DB browser) |
pnpm admin:promote <email> |
Promote user to admin |
pnpm tileset:create |
Download sprites + generate tileset |
The Monarium source code is released under the MIT License — see LICENSE for the full text. You may use, modify, and redistribute it (including commercially) provided the copyright notice and license text stay attached.
The MIT License covers the code I wrote. Other elements bundled in this repo have their own terms, and those terms take precedence for the material they cover:
- Pokémon names, sprites, and trademarks are property of Nintendo / Game Freak / Creatures Inc. / The Pokémon Company. Monarium is a personal, non-commercial fan project and is not affiliated with, endorsed, sponsored, or approved by any of them.
- Sprite data sourced from PokéAPI/sprites — the repository is distributed under CC0 1.0, but the sprite images themselves remain © The Pokémon Company (community fan use).
- Bundled fonts and textures each have their own license — several (Naver Nanum Son Geulssi, Maniackers Design Holiday MDJP, f0nt.com Book Akhanake) are freeware with restrictions that may affect commercial redistribution.
See the in-app /credits page (or the source file) for the complete list of third-party resources, their authors, and licenses. Before deploying Monarium publicly or commercially, check each entry — a few restrict bundling or require attribution that doesn't come automatically with MIT.
