A friend of mine runs a dental practice. It's a real, busy clinic, not a side hustle, and it works fine day to day. But she had one nagging problem: keeping track of each patient's teeth over time.
Think about it. A patient comes in, gets a filling on one tooth, comes back six months later with something on another. Which teeth were treated? What was the condition last time? On paper, that history lives across a bunch of folders and a phone full of intraoral photos with no real order. When you've got hundreds of patients, "what's the status of this person's teeth?" becomes a genuinely annoying question to answer.
So I offered to build her something. Here's how it went.
What actually needed solving
The temptation was to build "a clinic app" and end up with a generic dashboard nobody wants to use. Instead I focused on the actual pain: seeing a patient's dental status at a glance, and never losing the history.
Turns out a dental record is pretty specific. Each patient needs:
- basic info + anamnesis (allergies, medical history)
- an odontogram, the tooth chart dentists use to mark the condition of every tooth
- a history of visits with notes
- photos (intraoral, extraoral, radiographs) tied to the visit they came from
The visit notes follow a standard dentists already use called SOAP. Getting these details right early is what kept it from feeling like a spreadsheet with extra clicks.
The stack
Next.js and TypeScript for the app, Supabase for the backend (database, login, file storage, and access rules all in one), Tailwind + shadcn/ui for the interface, Vercel for hosting.
Supabase mattered here because this is real patient data, not a to-do list. It lets me lock down "who can see what" at the database level instead of just trusting the frontend to behave. Photos live in a private bucket and are only handed out through temporary links. There are three staff roles (dentist, receptionist, nurse), and only clinical staff can touch diagnoses and the tooth chart. That felt like the right floor for medical info.
The fun part: the tooth chart
I figured I'd just grab a library for the odontogram. Nope. There's no good, maintained one for React. So I built it from scratch in SVG.
My first version was too basic: click a tooth, color it in. Not good enough. A real odontogram marks conditions per surface: every tooth has five (mesial, distal, occlusal, buccal, lingual), and a dentist records a cavity or filling on a specific surface, not the whole tooth. There's also a separate set of whole-tooth states: missing, crown, implant, root remnant, and so on.
There's even a sneaky detail: "mesial" is the surface facing the middle of the mouth, so which side that is flips depending on which quarter of the jaw you're in. Once I handled that, the chart started looking like something a dentist recognizes instantly instead of a toy. That's the part I'm most proud of, and it's exactly what fixes her original problem. Now a patient's whole dental status is one screen.
A face for the clinic too
While I was at it, I added a public homepage (services, the dentists, hours, location) and an online booking form where someone can request an appointment (pick a date and morning/afternoon), no account needed.
That created a neat little puzzle: the booking form is public, but everything behind it is private patient data. So the public can create a booking request but can never read anyone's. Staff get a scheduling board with a calendar (dates with bookings show a dot), search, one-tap WhatsApp to confirm, rescheduling, and a way to link a request to an existing patient.
Making it not look like an admin panel
I wanted it to feel like an actual product. Calm clinical vibe (teal on soft off-white, dark sidebar) with Bricolage Grotesque for headings and IBM Plex for everything else. My favorite touch: record numbers, tooth numbers, and stats are all in a monospace font, so the whole thing reads a bit like a real dental chart. Tiny decision, big difference.
What I took from it
- The best hour I spent wasn't coding: it was learning how a dental record actually works. Everything after that got easier.
- With real patient data, "who can see what" has to be baked in from the start, not bolted on later.
- Sometimes there's no library and you just build the thing yourself. The tooth chart is proof it's worth it.
It's still a work in progress (reminders and PDF exports are on the list), but it already does the job it was meant to: her team can pull up any patient and see exactly what's going on with their teeth. Building something a friend uses every day beats any tutorial project.