Creative Foundary
Creative Foundary is the showcase for a post-production studio: a video-led, full-page scroll site that lets the work play, backed by a custom CMS so the team edits every section themselves. We built the whole thing on Next.js 16 and SQLite, reading the database directly on the server for a fast first paint and gating the admin behind JWT cookies.

- Role
- Design & full-stack build
- Domain
- Post-production studio
- Surfaces
- Showcase · CMS · API
- Core stack
- Next.js 16 · SQLite
The brief
A post-production studio lives or dies on its reel, so the site had to put the work first: video that plays, a full-page scroll that moves like an edit, and as little chrome as possible between a visitor and the footage.
The second requirement was independence. The studio changes its showreel, services, and client list constantly, and none of that should need a developer. So every section a visitor sees is backed by an editor in a custom CMS.
A server that reads the database directly
The public page is a Next.js server component that reads SQLite directly through a single shared connection, with no HTTP round-trip and no client-side fetching. The whole homepage is server-rendered on first load from typed database reads, then hydrated into an interactive client view.
The CMS is the mirror image: admin pages are client components that call auth-protected API routes, which verify a JWT cookie before reading or writing the same database. Public reads are direct and fast; writes go through the guarded API.
- Server Component reads SQLite directly
- No client fetching on the public site
- Admin → API → DB, JWT-guarded
- Clean SSR / client / route-handler split
The showcase
The front of house is built for footage. A hero video sets the tone, a dual-shelf carousel splits broadcast work from reels, and a services list expands inline to reveal detail without leaving the flow. Framer Motion carries the transitions, and a full-page scroll with a dedicated mobile nav keeps it coherent on every screen.
Around the reel sit the things that convert: an about section with bio and skill tags, a scrolling client-logo marquee, and a contact form whose service chips are themselves editable content.
- Hero video + full-page scroll
- Dual-shelf gallery: broadcast / reels
- Inline-expanding services
- Logo marquee + editable contact form
A CMS for every section
Every section maps to an editor: the hero video and welcome text, service tabs and their bullet points, the gallery shelves, the about content and portrait, the logo marquee, and the contact-form chips. Nothing on the public site is hard-coded.
Ordering is first-class. Every sortable list reorders by drag, and the API writes the new order for all rows in a single transaction. Uploads are drag-and-drop, destructive actions go through an accessible confirm dialog rather than a browser prompt, and incoming contact messages are managed with a read and unread flag.
- An editor per public section
- Drag reorder in one transaction
- Drag-and-drop file uploads
- Read / unread contact inbox
Data, auth, and deployment
Persistence is a single SQLite database in WAL mode with foreign keys on, opened once per process and initialised on first connect. Schema changes ship as inline, re-runnable migrations, singleton rows are enforced with a CHECK constraint, and rate-limit buckets live in the database itself so limits survive restarts.
Admin access is a bcrypt login that issues an HS256 JWT in an httpOnly cookie, checked before any admin route renders. The whole app ships as a multi-stage Alpine Docker image and generates its own JWT secret if one isn't supplied.
- SQLite WAL + foreign keys, one connection
- Re-runnable inline migrations
- Database-backed rate limiting
- JWT-cookie auth + Dockerised deploy
Deep dive
From SQLite to first paint, no API hop
Most stacks put an API between the page and the database even when both live in the same process. The public site skips that: the server component reads SQLite directly, so the homepage arrives fully rendered with zero client fetches.
- 1
One connection
A single SQLite connection is opened per process and reused, with the schema initialised on first connect.
- 2
The server reads, typed
The homepage server component calls the database helpers directly and gets typed rows back, with no fetch and no serialization round-trip.
- 3
Rendered, then hydrated
Those rows render to HTML on the server and pass as typed props into the client view, which hydrates for interaction.
- 4
Writes take the guarded path
Only the CMS goes over HTTP: admin pages call API routes that verify the JWT cookie before touching the same database.
- 5
Ordering in one transaction
Reorder endpoints accept the full ordered list of ids and update every row's sort order inside a single transaction, so the public list can never read a half-applied order.
What carried the build
Zero-fetch public site
The homepage is a server component that reads SQLite directly, so first paint needs no API and no client-side data fetching.
Every section is editable
A custom CMS backs the hero, services, gallery, about, logos, and contact form, so the studio runs its own site end to end.
Atomic reordering
Drag-to-reorder writes the whole new order in a single transaction, so the live site never shows a half-sorted list.
Self-contained deploy
SQLite in WAL mode, database-backed rate limits, and JWT-cookie auth ship as one multi-stage Alpine Docker image.
Built with
Frontend
- Next.js 16
- React 19
- Framer Motion
- Tailwind
Backend
- Route Handlers
- SQLite
- better-sqlite3
Auth & email
- JWT (jose)
- bcrypt
- Mailjet
Ops
- Docker
- Alpine