Inspiration
Prescription labels are written for pharmacists, not patients. The average label is packed with jargon — "contraindicated," "administer," "adverse effects" — language that assumes a medical education. For the 36 million adults in the US with low health literacy, elderly patients, and the millions who speak English as a second language, this isn't a minor inconvenience. Missed doses, accidental overdoses, and drug interactions are real consequences of not understanding a label. We wanted to build something that could sit between the pharmacy and the patient — a tool that takes a photo and hands back something a 10-year-old could understand, in whatever language they speak.
What it does
MedLit lets you photograph or upload a prescription label and get back a clean, plain-language medication card in seconds. The app:
Extracts all medication data from the label image using Claude's Vision API Rewrites every field at a Grade 5 reading level, replacing jargon with daily-life language ("take with breakfast" instead of "administer orally with food") Color-codes warnings — red for stop-and-seek-help conditions, amber for cautions like food or alcohol interactions, green for normal usage notes Simulates missed doses — tap "Missed a dose?" to get drug-specific guidance on what to do and what the risk is Switches language on demand across 10 languages including Spanish, Chinese, Arabic, and Hindi — no page reload Emails the card directly to the patient via Gmail SMTP so they have it on their phone.
How we built it
MedLit is a Next.js 15 full-stack app running entirely on localhost. The architecture centers on a two-pass Claude API pattern: Pass 1 — Vision extraction: The prescription image is base64-encoded in the browser and sent to /api/extract. Claude Vision reads the label and returns structured JSON — drug name, dose, frequency, warnings, interactions, and detected language. Pass 2 — Plain-language rewrite: The extracted JSON is passed to /api/explain with a strict system prompt that enforces a jargon blocklist, requires daily-life time anchors, caps sentence length at 15 words, and adds a flags array with color-coded warnings. We implemented a Flesch-Kincaid readability check in TypeScript and retry the pass if the output scores above Grade 6. Additional Claude calls power the skip simulator (/api/skip-sim) and the language switcher (/api/switch-lang). Email delivery uses Nodemailer over Gmail SMTP with an app password — no external email service required.
Challenges we ran into
Null fields from Claude: Real prescription labels are inconsistent. Claude occasionally returns null for fields like frequency or interactions when the label is ambiguous. We had to defensively guard every component against nulls after hitting runtime crashes in the card renderer. ESM package incompatibility: The text-readability npm package is ESM-only and broke under Next.js 15's module system. Rather than fighting the bundler, we rewrote the Flesch-Kincaid formula directly in TypeScript — removing the dependency entirely. Next.js version vulnerabilities: Starting from Next.js 14.2.3 flagged 17 CVEs including a critical authorization bypass. Upgrading to 15 also required migrating experimental.serverComponentsExternalPackages to serverExternalPackages. Prompt reliability: Getting Claude to return only valid JSON with no markdown fences or preamble required careful prompt engineering. We strip code fences as a fallback but the system prompts had to be explicit about this on every route.
Accomplishments that we're proud of
The two-pass Claude architecture produces genuinely readable output — the rewritten cards feel like something a caring nurse would say, not a legal disclaimer The readability retry loop is a real quality gate: if Claude's first attempt is still too complex, it automatically tries again Zero external databases or cloud infrastructure — the entire app runs on localhost:3000 with a single API key Language switching re-renders the full card in under 3 seconds for all 10 supported languages without a page reload
What we learned
Claude Vision is remarkably good at reading prescription labels even from low-quality phone photos — blurry, angled, partially obscured Prompt engineering for structured JSON output is its own discipline: the difference between getting clean JSON every time and getting markdown-wrapped JSON half the time comes down to a single sentence in the system prompt Next.js App Router's server-side route handlers are a clean pattern for AI backends — no separate Express server, no CORS configuration, one npm run dev Readability scoring is more nuanced than a single number. A text can score "easy" on Flesch-Kincaid while still containing one jargon word that confuses the reader — the blocklist in the prompt is as important as the score check
What's next for Medlit
Scheduled reminders — a cron-based reminder system (currently the email sends once on demand) with morning/evening slots tied to the detected dose schedule Pill identifier — extend the vision pass to identify pills by shape, color, and imprint for patients who lose their labels Pharmacy integration — accept structured HL7 FHIR prescription data directly, bypassing the OCR step for digital prescriptions Offline PWA — cache the last generated card as a service worker so patients can reference it without internet access Voice readout — text-to-speech for the card content, critical for low-literacy and visually impaired users
Built With
- anthropic
- api
- claude
- css
- gmail
- next.js
- node.js
- nodemailer
- react
- sdk
- smtp
- tailwind
- typescript

Log in or sign up for Devpost to join the conversation.