A simple CRUD task manager built with Next.js (App Router) and Supabase as the database. Each task has a title and a description, and you can create, read, update, and delete tasks.
All database work is done through Server Actions — functions in app/task/helper.ts marked with 'use server' that run on the server, never in the browser.
- Next.js 16 (App Router, Server Components, Server Actions)
- React 19
- Supabase (
@supabase/supabase-js) - TypeScript
- Tailwind CSS
- Go to supabase.com and sign in.
- Click New Project, give it a name, set a database password, and choose a region.
- Wait for the project to finish provisioning.
In the Supabase dashboard, open the SQL Editor and run:
create table tasks (
id bigint generated always as identity primary key,
title text not null,
description text,
created_at timestamptz default now()
);Or use the Table Editor UI to create a table named tasks with these columns:
| Column | Type | Notes |
|---|---|---|
id |
int8 |
Primary key, auto-generated |
title |
text |
Required |
description |
text |
Optional |
created_at |
timestamptz |
Default now() |
The app reads tasks ordered by
created_at, so keep that column.
The Supabase anon key is read from an environment variable. Create a .env.local file in the project root:
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-keyYou can find the anon key in the Supabase dashboard under Project Settings → API.
The Supabase project URL is currently set in
app/superbase-client.ts. Update it there to point at your own project.
npm install
npm run devOpen http://localhost:3000/task to use the task manager.
| File | Role |
|---|---|
app/task/page.tsx |
Server Component that lists tasks and renders the form |
app/task/helper.ts |
Server Actions ('use server') — all run on the server |
app/task/DeleteButton.tsx |
Reusable button (CustomButton) that submits a form to an action |
app/superbase-client.ts |
Initializes the Supabase client |
The functions in app/task/helper.ts run on the server, not in the browser:
createTask(formData)— inserts a new taskgetTasks()— reads all tasks, ordered by creation timeupdateTask(formData)— updates an existing taskdeleteTask(formData)— removes a taskeditTask(formData)— redirects to/task?edit=<id>to put the form in edit mode
Each one is wired to a <form action={...}>, so it receives a FormData object. After a mutation it calls revalidatePath("/task") (or redirects) so the list refreshes.
| Command | Description |
|---|---|
npm run dev |
Start the dev server |
npm run build |
Build for production |
npm run start |
Run the production build |
npm run lint |
Run ESLint |