A modern, minimalist todo application built with Next.js and Supabase, featuring per-user task management, optimistic UI updates, and comprehensive filtering/sorting capabilities.
- Secure Authentication: Built-in authentication with Supabase Auth
- Per-User Tasks: Row-Level Security ensures users only see their own tasks
- Optimistic UI: Instant feedback with graceful error handling
- Advanced Filtering: Filter by status, priority, due date, and search by title
- Flexible Sorting: Sort by any column (title, status, priority, due date, updated date)
- Responsive Design: Works seamlessly on desktop and mobile devices
- Real-time Updates: React Query ensures data consistency across tabs
- Modern UI: Built with shadcn/ui components and Tailwind CSS
- Frontend: Next.js 15, React 19, TypeScript
- Backend: Supabase (PostgreSQL + Auth + RLS)
- UI: Tailwind CSS, shadcn/ui, Radix UI primitives
- State Management: TanStack Query (React Query), Zustand for local state
- Date Handling: date-fns
- Table: TanStack Table
- Node.js 18+ and npm
- A Supabase project
git clone <your-repo>
cd nextjs-with-supabase
npm installCreate a .env.local file:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY=your_supabase_anon_keyRun the following SQL in your Supabase SQL editor to set up the database schema:
-- Create enum types for todo status and priority
CREATE TYPE todo_status AS ENUM ('todo', 'in_progress', 'done', 'blocked');
CREATE TYPE todo_priority AS ENUM ('low', 'medium', 'high', 'urgent');
-- Create todos table
CREATE TABLE IF NOT EXISTS public.todos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
title TEXT NOT NULL CHECK (char_length(title) BETWEEN 1 AND 140),
description TEXT,
status todo_status NOT NULL DEFAULT 'todo',
due_date TIMESTAMPTZ,
priority todo_priority NOT NULL DEFAULT 'medium',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_todos_user_id ON public.todos(user_id);
CREATE INDEX IF NOT EXISTS idx_todos_due ON public.todos(due_date DESC NULLS LAST);
CREATE INDEX IF NOT EXISTS idx_todos_status ON public.todos(status);
CREATE INDEX IF NOT EXISTS idx_todos_priority ON public.todos(priority DESC);
-- Enable trigram extension for text search (if not already enabled)
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX IF NOT EXISTS idx_todos_title_trgm ON public.todos USING gin (title gin_trgm_ops);
-- Enable Row Level Security
ALTER TABLE public.todos ENABLE ROW LEVEL SECURITY;
-- Create RLS policies
CREATE POLICY "select_own" ON public.todos FOR SELECT USING (user_id = auth.uid());
CREATE POLICY "insert_own" ON public.todos FOR INSERT WITH CHECK (user_id = auth.uid());
CREATE POLICY "update_own" ON public.todos FOR UPDATE USING (user_id = auth.uid());
CREATE POLICY "delete_own" ON public.todos FOR DELETE USING (user_id = auth.uid());
-- Create function to automatically update updated_at timestamp
CREATE OR REPLACE FUNCTION public.set_updated_at() RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create trigger to automatically update updated_at
CREATE TRIGGER set_updated_at
BEFORE UPDATE ON public.todos
FOR EACH ROW
EXECUTE PROCEDURE public.set_updated_at();npm run devOpen http://localhost:3000 to view the application.
├── app/ # Next.js app directory
│ ├── auth/ # Authentication pages
│ ├── protected/ # Protected todo dashboard
│ ├── layout.tsx # Root layout with providers
│ └── page.tsx # Landing page
├── components/ # React components
│ ├── ui/ # shadcn/ui components
│ ├── todo-dashboard.tsx # Main dashboard
│ ├── todo-table.tsx # Data table with sorting
│ ├── todo-filters.tsx # Filtering sidebar
│ ├── todo-quick-add.tsx # Quick add form
│ └── todo-edit-dialog.tsx # Edit modal
├── lib/ # Utilities and configurations
│ ├── hooks/ # Custom hooks (todo operations)
│ ├── providers/ # React Query provider
│ ├── types/ # TypeScript type definitions
│ └── supabase/ # Supabase client configuration
└── supabase/
└── migrations/ # Database schema
- Quick Add: Fast todo creation with expandable detailed form
- Inline Editing: Update status and priority directly in the table
- Full Edit Modal: Comprehensive editing with all fields
- Optimistic Updates: Instant UI feedback with rollback on errors
- Status Filters: Filter by todo, in_progress, done, blocked
- Priority Filters: Filter by low, medium, high, urgent priority levels
- Date Presets: Quick filters for "Due Today", "This Week", "Overdue"
- Text Search: Search todos by title with trigram matching
- Multi-Column Sorting: Sort by any column with visual indicators
- Responsive Design: Optimized for mobile and desktop
- Dark/Light Mode: Theme switching with system preference detection
- Loading States: Skeleton loaders and loading indicators
- Error Handling: User-friendly error messages with retry options
- Empty States: Helpful messaging when no todos exist
- Row-Level Security: Users can only access their own todos
- Real-time Sync: React Query ensures data consistency
- Optimistic UI: Instant updates with server synchronization
- Data Validation: Client and server-side validation
npm run dev- Start development servernpm run build- Build for productionnpm run start- Start production servernpm run lint- Run ESLint
- @tanstack/react-query - Server state management with optimistic updates
- @tanstack/react-table - Powerful table with sorting and filtering
- @radix-ui/ - Accessible UI primitives
- date-fns - Date manipulation and formatting
- zustand - Lightweight state management
- Optimistic UI: All mutations use optimistic updates for better UX
- React Query: Centralized server state management with caching
- Row-Level Security: Database-level security for multi-tenant data
- Component Composition: Modular components for maintainability
- TypeScript: Full type safety throughout the application
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
This project is licensed under the MIT License.