case study
lately
A diary platform where everyone keeps a notebook at their own URL
overview
Lately is a place to keep a diary. Anyone can sign up, pick a handle, and get a notebook at their own address with entries laid out like paper rather than a publishing platform. I built it with Next.js server components, MongoDB, and a CodeMirror-based markdown editor that saves itself.
problem
Most writing tools push you toward publishing: a post is either live or it isn't. A diary doesn't work like that. Some entries are unfinished, some are finished but meant only for you, and a few you want other people to read.
I wanted a small tool that handled those three states properly, kept the reading side fast and plain, and made writing feel like opening a notebook instead of filling in a CMS form.
approach
- 01I gave every entry three visibility states (draft, private, public) instead of a publish flag. Only public entries show up on the notebook, the shared archive and the sitemap; a private entry you don't own renders the 404 page.
- 02I made the reading side (front page, archive, notebooks, entries) server components end to end, so the HTML arrives with the entry already in it and markdown is rendered on the server. TanStack Query is only mounted on the archive, the desk and the auth forms, and it is seeded with initialData from the server render.
- 03I built the editor on CodeMirror 6 with markdown syntax highlighting, list handling, autocomplete, a live preview, and a debounced autosave. The slug and handle fields check availability as you type.
- 04I added [[wiki links]] between entries in the same notebook, resolved by slugifying the title, with backlinks shown on the target entry.
- 05Auth is a signed HS256 JWT in an httpOnly, sameSite=lax cookie (jose + bcryptjs). The edge proxy only sends signed-out visitors away from the writing desk; the real check is that every data-layer query filters by the owner's id.
features
Notebook per person
Each writer gets a public address under their handle, with a permanent link for every entry.
Draft, private, public
Three visibility states so finished-but-private entries are a normal case, not a workaround.
Self-saving markdown editor
A CodeMirror 6 editor with live preview and debounced autosave.
Sections and tags
Eight built-in sections such as About Today and Things I Learned, custom sections, and free-form tags.
Wiki links and backlinks
[[Entry title]] links to another entry in the same notebook, and the target lists what links to it.
Shared archive
A searchable archive of everything anyone has made public, filterable by section or tag.
challenges
One place that touches the database
Only two data-layer files issue MongoDB queries, and both are marked server-only so importing them from a client component fails the build. Query shapes and serialization live in one place instead of being spread across the codebase.
Stale cookies after account deletion
Clearing the cookie during page render threw, because Next.js forbids writing cookies there, and it broke every page for anyone with a stale session. I moved cookie clearing into a dedicated server handler, where Next.js does allow writing cookies.
Streaming vs. 404 status
A shared loading.tsx made every page stream, so missing entries could only return 200 with a noindex tag. I removed those skeletons from the reading side rather than make that behaviour worse.
outcome
Lately is live at lately.justtayyabkhan.com with a shared front page, archive and public notebooks, and I use it to keep my own notebook there.
stack
contact
Have something worth building?
Open to freelance. Fastest response on WhatsApp ↗ — usually within the hour.