Building & Deploying My First Web App
The Stack
- Next.js 16 — React framework that handles both frontend and backend
- Supabase — cloud database (PostgreSQL) + authentication
- Tailwind CSS v4 — utility-first styling
- Vercel — hosting and automatic deployment
- GitHub — version control
1. Database (Supabase)
Supabase gave us a cloud PostgreSQL database without needing to run our own server.
We created two tables:
notes— stores title, slug, content, pdf_url, video_url, board_idboards— stores kanban column names and positions
Row Level Security (RLS) was enabled on both tables:
- Public can read (anyone can view the site)
- Only authenticated users can write (only you can add/edit notes)
2. Authentication (Supabase Auth)
- Created an admin account in Supabase → Authentication → Users
- Used
@supabase/ssrto handle auth in Next.js via cookies - Two Supabase clients:
supabase-server.ts— reads cookies, used in Server Componentssupabase-browser.ts— used in Client Components
3. Next.js App Structure
app/ layout.tsx ← sticky header + footer, wraps every page page.tsx ← public home page (kanban board) admin/ page.tsx ← protected admin dashboard login/page.tsx ← login form notes/[slug]/ page.tsx ← individual note page components/ KanbanBoard.tsx ← public read-only kanban AdminKanban.tsx ← drag-and-drop admin kanban NoteForm.tsx ← create new notes EditNoteModal.tsx ← edit existing notes NoteContent.tsx ← renders Markdown LogoutButton.tsx ← signs out lib/ supabase.ts ← types + public client supabase-server.ts ← server-side auth client supabase-browser.ts ← browser-side auth client proxy.ts ← protects /admin routes (Next.js 16)
4. Key Next.js 16 Concepts
Server vs Client Components
- Server Components (default) — run on the server, can fetch data directly
- Client Components (
'use client') — run in the browser, needed for interactivity
The golden rule: you cannot pass functions as props from a Server Component to a Client Component.
App Router file conventions
layout.tsx— shared UI wrapping all child pagespage.tsx— the actual page content[slug]— dynamic route segment (e.g./notes/my-note)
Breaking changes in Next.js 16
paramsis now a Promise → mustawait paramsmiddleware.tsis renamed toproxy.ts, export renamed frommiddlewaretoproxy
5. Kanban Board
- Public board (
KanbanBoard.tsx) — CSS Grid withrepeat(auto-fit, minmax(260px, 1fr))so columns fill the screen automatically - Admin board (
AdminKanban.tsx) — uses@dnd-kit/corefor drag-and-drop - Notes have a
board_idlinking them to a column;nullmeans unassigned
6. Note Features
Each note can contain:
- Markdown content — rendered with
react-markdown+@tailwindcss/typography - YouTube embed — paste a YouTube URL, extracted with regex into an iframe
- PDF embed — paste a Supabase Storage public URL, displayed in an iframe
7. Deployment Pipeline
GitHub (Version Control)
- Every change is committed with
git commitand pushed withgit push - GitHub stores the full history of every change ever made
Vercel (Hosting + CI/CD)
- Connected to GitHub repo
- Every push to
mainautomatically triggers a new build and deployment - Environment variables (
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_ANON_KEY) are set in Vercel settings so the live app can talk to Supabase - This is called CI/CD — Continuous Integration / Continuous Deployment
The flow:
You edit code locally ↓ git push origin main ↓ Vercel detects the push ↓ Vercel runs npm run build ↓ If build passes → live site updates automatically If build fails → Vercel shows the error, previous version stays live
8. Key Lessons
.env.localstores secrets locally and is never committed to GitHub (in.gitignore)- Supabase environment variables must also be added to Vercel separately
npm install <package>must be run for every dependency — Vercel only installs what's listed inpackage.json- Local builds use Turbopack; always test with
npm run buildbefore pushing if unsure