Updated October 26, 2026 β refreshed features, stack, setup, and links.
TL;DR: I built an open-source blog CMS with Next.js, Supabase, and TipTap. It now includes a richer publishing workflow, comments and reactions, author profiles, newsletters, and account security features. The live site is The Practical Engineer.
- π Browse the live blog
- π Create an account
- π Get the source on GitHub
- π Read the documentation
An important update for returning readers: the standalone AI assistant and external Developer API described in the original article have been retired. Personal AI provider settings remain available; editor assistance is future work.
π€ Why I Built This
I wanted a practical place to write and publish without rebuilding authentication, permissions, an editor, and media handling for every project.
That idea grew into a full-stack CMS. The interesting work has increasingly been in the details: keeping drafts private, protecting published URLs, recovering writing, and making publication and newsletter delivery behave predictably when requests fail.
If you are learning Next.js and Supabase, those are useful problems to explore in a real codebase.
π§ What the CMS Can Do Today
π Accounts, roles, and security
The app uses Supabase Auth with email/password authentication and support for Google and GitHub sign-in when configured. It includes email confirmation, password recovery, authenticator-based MFA, and onboarding for new accounts.
Authors manage their own posts. Administrators manage users, posts, comments, and taxonomy. Authorization combines database Row Level Security with server-side identity, ownership, and role checks. Public author profiles expose a limited public view while private account data stays separate.
βοΈ A fuller writing and publishing workflow
The TipTap rich-text editor supports formatting, links, tables, task lists, and images. Authors can organize posts with existing categories and searchable tags, add cover and inline images, and configure SEO metadata.
The workflow includes:
- Draft saving, writing recovery, and a private preview of unsaved content.
- A publication review step with server-side readiness checks and explicit human editorial confirmation.
- Atomic post and taxonomy saves, plus conflict detection to protect against stale edits.
- Published URL protection: changing an article URL is deliberate, and former published URLs redirect to the current one.
- Search, filters, sorting, pagination, and bulk unpublish/delete actions in the dashboard.
Previewing does not make a draft public or send a newsletter. Publishing checks content readiness, but a human still needs to review accuracy, usefulness, attribution, and metadata.
π¬ Community and discovery
Comments are now implemented, along with comment reactions and five article reaction types: Love, Unicorn, Mind blown, Celebrate, and Fire.
Readers can explore public author profiles and category/tag pages. The app also includes reading-time estimates, SEO metadata, canonical URLs, and sitemap support. Comments and SEO are no longer just items on the original wishlist.
π¬ Newsletter subscriptions
Readers can subscribe from article pages. Publishing queues a newsletter notification through Resend with a configurable delay. The default delay is 60 minutes; delivery starts on a subsequent dispatcher run, rather than being guaranteed exactly an hour later.
Authors can see notification status in the editor. Unpublishing cancels pending delivery, and administrators have a newsletter dashboard with subscriber information and CSV export. Messages include unsubscribe links.
π€ What changed with AI and the API?
The standalone PDF upload, book chat, and full-post generation workflows are retired. The external Developer API and developer-key interface are retired too; old endpoints return 410 API_RETIRED.
Authors and administrators can still manage their own encrypted provider credentials in Settings β AI Providers. Credential verification checks provider access; it does not generate content. Administrators do not get access to another author's provider keys.
Editor assistance is tracked separately as future work. This project should no longer be described as offering a supported headless API or a working standalone AI writing assistant.
For details, see AI Providers and retirement and Developer API retirement.
π οΈ Current Tech Stack
These are the versions pinned in the repository when this update was written, rather than a claim about the latest upstream releases.
| Area | Technology |
|---|---|
| Application | Next.js 16.4.2 App Router, React 19.3.1, TypeScript 6.0.3 |
| Database, auth, media | Supabase Postgres, Auth, Storage, and RLS |
| Rich-text editor | TipTap 3.31.4 |
| UI | Tailwind CSS 4.3.3, shadcn/ui, Base UI, Lucide icons |
| Forms and validation | React Hook Form and Zod |
| Client state | Zustand |
| Resend | |
| Testing | Vitest, Testing Library, and Playwright |
| Deployment | Vercel, with cron-based newsletter dispatch |
| Local toolchain | Node 24.21.0 and pnpm 10.29.3 |
OpenAI, Anthropic, and Google SDK dependencies remain in the repository for provider settings and future work. Their presence does not mean content generation is currently available.
π Run It Locally
You will need the pinned Node and pnpm versions, plus Docker for local Supabase.
git clone https://github.com/thepracticalengineer/nextjs-blog-cms.git
cd nextjs-blog-cms
nvm install
nvm use
corepack enable
pnpm install --frozen-lockfile
pnpm dlx supabase@2.119.0 start
Then follow the local development guide to configure your local Supabase credentials and environment. Use the complete ordered migration history; the historical schema snapshot alone is not enough.
Once configuration is ready:
pnpm run dev
Open http://localhost:3000. Use a disposable local database for tests and keep server-side secrets out of client code and Git.
The testing guide covers unit and browser/API verification. For deployment, read the migration prerequisites and deployment guide.
π§ Whatβs Next
The current roadmap includes:
- Editor assistance.
- An analytics dashboard.
- Scheduled article publication.
- Multi-author collaboration.
These are planned features. Newsletter dispatch scheduling already exists, but scheduled article publication is a separate capability.
Check the roadmap and GitHub issues for the evolving status.
π¦ Open Source β Try It, Fork It, Improve It
The project is MIT-licensed. Use it as a starting point for your own blog or to study how an App Router application handles publishing, permissions, rich text, and background email delivery.
If you try it, I would love feedback on the writing experience and the parts that are confusing. Bug reports and focused contributions help make it better.
β Star or fork the repository
Happy building π¨βπ»
Top comments (2)
Curious how you're handling the RLS policies when an Author gets promoted to Admin mid-session β does the Supabase client pick up the role change immediately, or does it require a fresh JWT before the new permissions kick in?
Thanks for the question, Hamed β and sorry for the late reply! In the current implementation, Admin/Author is stored in
public.profiles.role, rather than as an application-role claim in the JWT. The role-aware RLS checks look up that row usingauth.uid(), and server authorization reads the current profile before checking permissions.So once the promotion commits, subsequent authorization checks can see the new role using the existing valid session; a fresh JWT isn't required just for the role change. The same applies to demotion for subsequent checks (it doesn't retroactively cancel an operation already in flight).
The UI is a separate concern: an already-open dashboard can still show stale navigation/profile state until it reloads or explicitly refreshes that data. There isn't a live role-change subscription, and token refresh alone doesn't refetch the client profile. That cached UI state never grants server permissions.
Here's the current server authorization code and the client profile refresh behavior. I've also updated the article to reflect the current project.