Portfolio Site
This site itself: a Next.js portfolio with a Supabase-backed admin CMS, bilingual content, and a static seed fallback so it still renders with no database at all.
- Next.js
- React
- TypeScript
- Tailwind CSS
- Supabase
- Framer Motion
OVERVIEW
Every section of this site, from profile and projects to experience, research and skills, is a Supabase row edited from /admin, and an edit goes live within a minute. If Supabase is unreachable or not configured, the site renders from a static seed file, so it never shows an empty page. One schema file drives the admin form, the list of columns a save may write, and type conversion, so adding a field takes one line. Access to the admin is checked in middleware, in the admin layout, and by Postgres row-level security, and the app only uses the public anon key. Content is stored in English and Indonesian and switches without separate routes.
WHAT I DID
- Content-as-data: every section is a Supabase row edited from /admin with 60-second ISR, and the whole site still renders from a static seed file if Supabase is unreachable or unconfigured.
- One field schema drives the admin form renderer, the column whitelist and the type coercion for every table — a new field is one line, not a form plus a validator plus an action.
- Auth enforced in three layers: middleware refreshes the session, the admin layout re-checks membership, and Postgres RLS is the actual source of truth — only the anon key is used, no service-role key in the app.
- EN/ID with no duplicated routes: every text field is stored as an _en/_id pair and swapped client-side; a small head script sets the theme before hydration so there is no light/dark flash.


