← All projects

SumitGautam.tech

The site you're reading — a Next.js portfolio with its own CMS, an AI assistant grounded in my resume data, and deliberate failure modes throughout.

Solo build · Jan 2022 – Present (rebuilt Aug 2026)

Content editable without a deploy
Renders fully without its database
AI assistant reads the same data as the pages
Every subsystem degrades independently

The problem

A portfolio built as static markup goes stale. Every content change means editing code and redeploying, so in practice the updates never happen — and an out-of-date portfolio is worse than not having one.

I also wanted the site to answer questions about my work directly, rather than making a visitor read five pages to find out whether I've used Kubernetes. The risk with that is an assistant that confidently says something my resume contradicts, so whatever answered questions had to read from the same source of truth as the site itself.

The last constraint was cost. This runs on free tiers, which means every external dependency is one I should assume will rate-limit, stall, or disappear.

What I built

The site was migrated from Create React App with HashRouter to the Next.js 16 App Router, and content moved out of a JavaScript file into Supabase.

All content is fetched server-side once in the root layout and passed down through React context, so client components never talk to Supabase directly and the page arrives fully rendered. Postgres row-level security allows public reads and restricts writes to authenticated sessions. A complete static copy of the content ships in the bundle as a fallback, so the site renders correctly even if the database is unreachable or the environment variables are missing.

Content is edited through a separate Next.js admin application deployed independently, covering every section including this case study. Updating the site requires no deploy.

The CV page renders an A4-styled card and uses print stylesheets to produce a clean PDF through the browser, with no PDF library and no separate document to keep in sync. The contact form posts to a route that sends mail through Resend with reply-to set to the sender.

The AI assistant builds its system prompt from the same Supabase tables that render the site — resume summary, roles, skills, education and these case studies — so it cannot drift from what the pages say. It streams responses from Google Gemini, retries only before the first byte has been sent (once a visitor is mid-sentence, a restart would corrupt the answer), applies a hard per-attempt timeout with a fallback model, and distinguishes exhausted quota from transient failure so it stops retrying when retrying is pointless. Requests are rate-limited per IP by an atomic Postgres function that fails open, so a database problem degrades the assistant rather than taking it offline.

Outcome

The site is live on Vercel across two projects — the public site and the admin panel on its own subdomain — with content served from Supabase and editable without touching code.

Every subsystem fails independently and visibly rather than silently: without a database the site still renders from its static fallback, without a mail key the contact form returns a clear error while the rest of the page works, and without an AI key the chat widget reports itself unavailable and nothing else changes.

The assistant answers from live resume data, so updating a job description in the admin panel changes what it tells visitors within minutes, with no redeploy and no second copy of the content to maintain.

Stack

Next.js 16 (App Router) · React 19 · Supabase (Postgres + RLS) · styled-components · Google Gemini · Resend · Vercel