Cyber For AI Notes

Building & Deploying A Web App

End-to-end walkthrough of building a full-stack Next.js notes app from scratch

June 14, 2026

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_id
  • boards — 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/ssr to handle auth in Next.js via cookies
  • Two Supabase clients:
    • supabase-server.ts — reads cookies, used in Server Components
    • supabase-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 pages
  • page.tsx — the actual page content
  • [slug] — dynamic route segment (e.g. /notes/my-note)

Breaking changes in Next.js 16

  • params is now a Promise → must await params
  • middleware.ts is renamed to proxy.ts, export renamed from middleware to proxy

5. Kanban Board

  • Public board (KanbanBoard.tsx) — CSS Grid with repeat(auto-fit, minmax(260px, 1fr)) so columns fill the screen automatically
  • Admin board (AdminKanban.tsx) — uses @dnd-kit/core for drag-and-drop
  • Notes have a board_id linking them to a column; null means 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 commit and pushed with git push
  • GitHub stores the full history of every change ever made

Vercel (Hosting + CI/CD)

  • Connected to GitHub repo
  • Every push to main automatically 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.local stores 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 in package.json
  • Local builds use Turbopack; always test with npm run build before pushing if unsure