Case Study: A Next.js Portfolio With Its Own Blog CMS and Admin — No Monthly CMS Fees
The brief
A portfolio that I can update myself — blog posts, categories and projects — without:
- a monthly headless-CMS subscription,
- a separate backend to host,
- losing SEO or performance.
What was built
- Public site: portfolio,
/blogwith category filters,/projectswith case-study pages. - Private admin at
/admin: dashboard, post/category/project editors, Markdown editor with live preview, draft/publish/schedule, noindex toggle. - Image pipeline: direct uploads to Cloudinary, served in AVIF/WebP at the right size.
- SEO: per-page metadata, canonical URLs, Open Graph images, JSON-LD (
BlogPosting,BreadcrumbList,SoftwareSourceCode), database-driven sitemap and robots.
Stack
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 15 App Router | SEO pages + admin in one app |
| Database | PostgreSQL (Neon) via Prisma | Serverless, free tier, typed queries |
| Images | Cloudinary | Resizing and format conversion without using Vercel's image quota |
| Auth | Signed session cookie + scrypt password hash | Single admin; no auth vendor needed |
| Hosting | Vercel | Git-push deploys, previews per branch |
Key decisions
- ISR for articles. Posts are static and refreshed on publish via
revalidatePath(hourly fallback). Fast like a static site, fresh like a CMS. - Defence in depth on
/admin. Middleware blocks unauthenticated requests early; every page, server action and upload route re-checks the session. - Validation shared across UI and scripts. One pure validation module is used by the admin form, the server actions, bulk-publishing scripts and the tests.
- Migrations in the build. Production deploys apply database migrations automatically; preview deploys never touch the live schema.
- Media cleanup. Removing an image from a post deletes it from Cloudinary only when nothing else references it.
Results
- No CMS subscription — hosting fits free/low tiers of Vercel, Neon and Cloudinary.
- Publishing takes minutes from the admin; changes go live without a redeploy.
- Crawlers get clean pages: canonical URLs, structured data, sitemap, and
/admin+/apiblocked. - 47 automated tests cover auth, validation, slugs, SEO output and media rules.
When this approach fits your business
Good fit: a company site, portfolio or product blog with one or a few editors, where SEO matters and you want no recurring CMS fees.
Not a fit: large editorial teams needing workflows, roles and localisation — a headless CMS earns its cost there.
2027 outlook (my predictions)
- More small businesses drop paid CMS plans for in-app admins, as AI tooling makes custom admin panels cheaper to build.
- Structured data becomes a requirement, not a bonus, as AI search uses it to pick sources.
- Owned channels beat rented ones — a site you control, with your own content, matters more as platforms and AI summaries take traffic.
Want a site you can manage yourself?
I build Next.js sites with a built-in admin, image uploads and SEO — no monthly CMS fees. Browse more projects and case studies, or get in touch.
