Apilar tareas de forma inteligente — A production-ready Kanban board SaaS built with Next.js, PostgreSQL, and Prisma.
Created by Marco Fernandez
Stackly is a full-featured Kanban board application inspired by Trello. It allows teams to organize tasks visually using boards, columns, and cards with full drag-and-drop support, real-time updates, multi-language UI, and theme customization.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript 5 |
| Database | PostgreSQL |
| ORM | Prisma 6 |
| Auth | NextAuth.js v4 (JWT) |
| State | Zustand |
| Drag & Drop | @dnd-kit/core + @dnd-kit/sortable |
| Styling | Tailwind CSS 3 |
| UI Primitives | Radix UI |
| Icons | Lucide React |
| Forms | React Hook Form + Zod |
| Toasts | React Hot Toast |
| Password Hashing | bcryptjs |
- Node.js 18+
- PostgreSQL database
# 1. Install dependencies
npm install
# 2. Configure environment
cp .env.example .env
# Set DATABASE_URL, NEXTAUTH_SECRET, NEXTAUTH_URL
# 3. Push schema and generate client
npx prisma db push
npx prisma generate
# 4. (Optional) Seed demo data
npx ts-node prisma/seed.ts
# 5. Start development server
npm run devOpen http://localhost:3000.
| Password | |
|---|---|
| alice@stackly.dev | password123 |
| bob@stackly.dev | password123 |
- Email & Password Login — credentials-based authentication via NextAuth.js
- User Registration — create a new account with name, email, and password
- JWT Sessions — stateless session management, no database session table needed
- Password Hashing — bcryptjs with 12 salt rounds
- Protected Routes — all dashboard routes require an active session; unauthenticated users are redirected to
/login - Sign Out — clears the session token
- Create Boards — give a board a title, optional description, and a background color from 12 presets
- Board Grid — the dashboard shows all boards the user owns or is a member of
- Board Background Colors — each board has its own accent color applied to the header area
- Auto-created Labels — new boards get 3 default labels: Bug, Feature, Enhancement
- Edit Boards — rename or change the color of any board you own
- Delete Boards — permanently deletes the board and all its columns, cards, and data
- Board Access Control — boards can be public or private; only members can access private boards
- Empty State — friendly prompt to create the first board if none exist yet
- Add Columns — inline form to add new columns/lists to a board
- Rename Columns — double-click the column title or use the dropdown menu to rename
- Delete Columns — removes the column and all its cards
- Drag to Reorder — columns can be dragged horizontally to any position; order persists to the database
- Card Count Badge — each column header shows how many cards it contains
- Float-based Ordering — column positions are stored as floats to allow arbitrary reordering without renumbering
Each card supports the following properties:
| Property | Details |
|---|---|
| Title | Editable inline in the detail modal |
| Description | Multi-line text, click to edit |
| Priority | None / Low / Medium / High / Urgent — color-coded badge |
| Due Date | Date picker; overdue cards show a red indicator |
| Labels | Multiple labels per card, each with a custom color |
| Assignees | Assign one or more board members |
| Checklists | One or more checklists with individual items |
| Comments | Threaded comments with timestamps |
| Attachments | Schema and model in place |
| Archived | Cards can be archived instead of deleted |
Additional card features:
- Card Detail Modal — click any card to open a full editing modal
- Inline Title Editing — click the title in the modal to edit in place
- Inline Description Editing — click the description area to toggle an editable textarea
- Overdue Indicator — red alert icon and text when due date has passed
- Priority Badges — colored badges (None / Low / Medium / High / Urgent)
- Label Color Bars — label colors shown as chips on the card preview
- Search — filter cards by title using the search bar in the board header or topbar
- Delete Card — permanently delete a card with one click from the detail modal
- Optimistic Updates — UI updates instantly before the server responds; rolls back on failure
Built with @dnd-kit/core and @dnd-kit/sortable:
- Card Drag — drag cards between columns or reorder within the same column
- Column Drag — drag entire columns left/right to reorder them
- Drag Overlay — a semi-transparent clone of the dragged item follows the cursor
- Ghost Card — the original card position is shown as a faded ghost while dragging
- Closest Corners Algorithm — accurate drop detection even for small targets
- Touch Support — works on mobile devices (150 ms press delay, 5 px tolerance)
- Mouse Sensor — 5 px activation distance to prevent accidental drags on clicks
- Optimistic State — the board state updates immediately on drop; if the API call fails, the board reverts to the snapshot taken before the drag started
- Add Checklists — a card can have multiple named checklists
- Add Items — add text items to each checklist
- Toggle Items — check/uncheck items; updates persist to the database
- Delete Items — remove individual items
- Progress Bar — each checklist shows a percentage completion bar
- Item Count — completed vs total items shown as
X / Y
- Add Comments — type a comment and press Enter or click the button to post
- Instant Display — comments appear immediately after posting
- User Attribution — each comment shows the author's avatar and name
- Timestamps — comments show the relative time they were created
- Chronological Order — comments are listed oldest first
- Comment Count — shown next to the Comments section heading
- Board-Scoped — labels belong to a board, not globally, so each board can have its own set
- Multiple Labels Per Card — assign any number of labels to a card
- Custom Colors — each label has its own hex color
- Color Chips — labels appear as colored chips on the card preview and in the detail modal
- Default Labels — Bug (red), Feature (blue), Enhancement (green) are created automatically with each new board
- Board Membership — each board has a list of members
- Three Roles —
ADMIN,MEMBER, andVIEWER(role stored per board per user) - Member Avatars — up to 4 member avatars shown in the board header; overflow is indicated
- Invite Button — UI present in the board header; invite flow is extensible
- Card Assignees — any board member can be assigned to any card
- Activity Model — every card action (create, move, comment) can be logged to the Activity table
Four themes selectable from Settings → Appearance:
| Theme | Description |
|---|---|
| Light | Clean white background, default look |
| Dark | Dark navy background, muted text |
| System | Automatically follows the OS light/dark preference |
| Custom | User-defined primary color and background color |
Custom Theme:
- 12 preset primary color swatches
- Full color picker for primary color with hex input
- Full color picker for background color with hex input
- Live Preview — the entire page updates in real time as you drag the color picker, before saving
- Colors are saved to the database (
UserSettings.customPrimary,UserSettings.customBackground) and restored on next login
Theme Application:
- Themes apply immediately on click — no page reload needed
- Theme class is set on the
<html>element using CSS custom properties - Dark mode overrides Tailwind utility classes globally via
html.darkselectors
90+ languages supported with instant page-wide translation:
- English (US, UK, AU), Spanish (ES, MX, AR), French (FR, CA), German (DE, AT), Portuguese (BR, PT), Italian, Russian, Chinese (Simplified & Traditional), Japanese, Korean, Arabic, Hindi, Turkish, Polish, Dutch, Greek, Vietnamese, Indonesian, Thai, Ukrainian, Persian, Hebrew, Hungarian, Czech, Slovak, Romanian, Swedish, Norwegian, Danish, Finnish, Bulgarian, Croatian, Serbian, Latvian, Lithuanian, Estonian, and many more
How it works:
- Selecting a language from the list applies it instantly — every visible string on the page changes without a reload or a "Save" button
- The
AppSettingsProvidercontext holds the active translation object; any component usinguseAppSettings().t("key")re-renders automatically - The selected language is saved to the database and restored on next login
Date Formats (5 options):
| Format | Example |
|---|---|
| MM/DD/YYYY | 03/26/2026 |
| DD/MM/YYYY | 26/03/2026 |
| YYYY-MM-DD | 2026-03-26 |
| DD MMM YYYY | 26 Mar 2026 |
| MMM DD, YYYY | Mar 26, 2026 |
- View Profile — shows avatar, display name, email address, and account creation date
- Edit Display Name — update the name shown across the app
- Email (read-only) — email cannot be changed (used as the login identifier)
- Avatar — displays user image if set; falls back to initials with a generated background color
- Member Since — shows the date the account was created, formatted per the user's date format setting
Saved per-user to the database:
| Setting | Default | Description |
|---|---|---|
| Email Notifications | On | Receive email updates about boards and cards |
| Mention Alerts | On | Get notified when someone mentions you in a comment |
| Due Date Reminders | On | Reminder 24 hours before a card is due |
- Change Password — requires current password before setting a new one (min. 6 characters)
- Sign Out All Devices — invalidates the current session and redirects to login
- Delete Account — permanently deletes the user account, all owned boards, columns, cards, and data; requires typing
DELETEto confirm - Server-Side Validation — all API inputs are validated with Zod schemas
- Authorization Checks — board ownership and membership verified on every API call
- CSRF Protection — built into NextAuth.js
- Password Hashing — passwords are never stored in plain text
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/[...nextauth] |
NextAuth handler (login, session, logout) |
POST |
/api/register |
Create a new user account |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/boards |
List all boards for the current user |
POST |
/api/boards |
Create a new board |
GET |
/api/boards/:id |
Get board with all columns and cards |
PATCH |
/api/boards/:id |
Update board title, description, or color |
DELETE |
/api/boards/:id |
Delete a board and all its data |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/boards/:id/columns |
Create a new column |
PATCH |
/api/boards/:id/columns/:colId |
Rename a column |
DELETE |
/api/boards/:id/columns/:colId |
Delete a column and its cards |
PATCH |
/api/boards/:id/columns/reorder |
Bulk reorder all columns |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/cards |
Create a new card |
GET |
/api/cards/:id |
Get card details |
PATCH |
/api/cards/:id |
Update any card property |
DELETE |
/api/cards/:id |
Delete a card |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/cards/:id/comments |
Get all comments for a card |
POST |
/api/cards/:id/comments |
Add a comment to a card |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/checklists/:id/items |
Add an item to a checklist |
PATCH |
/api/checklists/:id/items/:itemId |
Toggle or update a checklist item |
DELETE |
/api/checklists/:id/items/:itemId |
Delete a checklist item |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/users/me |
Get the current user's profile |
PATCH |
/api/users/me |
Update the current user's name |
DELETE |
/api/users/me |
Delete the current user's account |
GET |
/api/users/settings |
Get user settings (creates defaults if none exist) |
PATCH |
/api/users/settings |
Update any user setting |
POST |
/api/users/change-password |
Change password (requires current password) |
User
├── id, name, email, password, image, createdAt
├── → Board[] (owned)
├── → BoardMember[]
├── → Comment[]
├── → UserSettings
└── → Notification[]
Board
├── id, title, description, background, isPublic
├── → Column[]
├── → BoardMember[]
└── → Label[]
Column
├── id, title, order, color
└── → Card[]
Card
├── id, title, description, order, dueDate
├── priority (NONE|LOW|MEDIUM|HIGH|URGENT)
├── archived
├── → Label[]
├── → User[] (assignees)
├── → Checklist[]
└── → Comment[]
UserSettings
├── emailNotifications, mentionAlerts, dueDateReminders
├── theme, language, dateFormat
└── customPrimary, customBackground
src/
├── app/
│ ├── (auth)/ # Login & Register pages
│ ├── (dashboard)/ # Protected dashboard pages
│ │ ├── dashboard/ # Board grid
│ │ ├── board/[id]/ # Kanban board view
│ │ ├── profile/ # User profile
│ │ └── settings/ # App settings
│ ├── api/ # API route handlers
│ └── globals.css # Theme CSS variables
├── components/
│ ├── board/ # Board, Column, Card, DnD components
│ ├── dashboard/ # Dashboard grid, create board modal
│ ├── layout/ # Sidebar, Topbar, UserMenu
│ └── ui/ # Button, Input, Modal, Avatar, Badge...
├── lib/
│ ├── auth.ts # NextAuth configuration
│ ├── prisma.ts # Prisma client singleton
│ ├── utils.ts # Helpers (cn, formatDate, isOverdue...)
│ ├── i18n/
│ │ ├── translations.ts # 90+ language translation objects
│ │ └── context.tsx # AppSettingsProvider + useAppSettings
│ └── validations/ # Zod schemas
├── store/
│ ├── board-store.ts # Zustand board state + optimistic updates
│ └── ui-store.ts # Modal open/close state
└── types/
└── index.ts # Shared TypeScript types
DATABASE_URL="postgresql://user:password@localhost:5432/stackly"
NEXTAUTH_SECRET="your-secret-key"
NEXTAUTH_URL="http://localhost:3000"Marco Fernandez
MIT