Back to Blog
·7 min

Kanban Project Management Template: Build a Task Management App

Project management tools are a staple SaaS category. Kanban boards, task tracking, team collaboration, and Gantt charts are features that every business needs. Building a project management app from scratch requires implementing drag-and-drop, real-time updates, user permissions, and notification systems.

A Kanban project management template provides all of this infrastructure so you can focus on your unique features.

Core Features

A production-ready project management template includes:

  • Kanban boards with drag-and-drop
  • Task creation with assignees and due dates
  • Project and workspace organization
  • Team member management
  • Activity tracking and notifications
  • File attachments
  • Time tracking
  • Reporting dashboards

The Project Manager Pro template includes all features with team collaboration.

Kanban Board Component

'use client'

import { useState } from 'react'
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'

interface Task {
  id: string
  title: string
  assignee?: string
  priority: 'low' | 'medium' | 'high'
}

interface Column {
  id: string
  title: string
  tasks: Task[]
}

export function KanbanBoard({ initialColumns }: { initialColumns: Column[] }) {
  const [columns, setColumns] = useState(initialColumns)

  const onDragEnd = (result: any) => {
    if (!result.destination) return

    const { source, destination } = result
    const newColumns = [...columns]

    const sourceCol = newColumns.find(c => c.id === source.droppableId)
    const destCol = newColumns.find(c => c.id === destination.droppableId)

    if (!sourceCol || !destCol) return

    const [movedTask] = sourceCol.tasks.splice(source.index, 1)
    destCol.tasks.splice(destination.index, 0, movedTask)

    setColumns(newColumns)
  }

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <div className="flex gap-4 overflow-x-auto pb-4">
        {columns.map(column => (
          <div key={column.id} className="w-72 flex-shrink-0">
            <h3 className="mb-3 font-semibold text-gray-700">
              {column.title}
              <span className="ml-2 text-sm text-gray-400">({column.tasks.length})</span>
            </h3>
            <Droppable droppableId={column.id}>
              {(provided) => (
                <div
                  ref={provided.innerRef}
                  {...provided.droppableProps}
                  className="min-h-[200px] space-y-2 rounded-xl bg-gray-50 p-3"
                >
                  {column.tasks.map((task, index) => (
                    <Draggable key={task.id} draggableId={task.id} index={index}>
                      {(provided) => (
                        <div
                          ref={provided.innerRef}
                          {...provided.draggableProps}
                          {...provided.dragHandleProps}
                          className="rounded-lg border bg-white p-3 shadow-sm"
                        >
                          <p className="text-sm font-medium">{task.title}</p>
                          {task.assignee && (
                            <p className="mt-1 text-xs text-gray-500">{task.assignee}</p>
                          )}
                        </div>
                      )}
                    </Draggable>
                  ))}
                  {provided.placeholder}
                </div>
              )}
            </Droppable>
          </div>
        ))}
      </div>
    </DragDropContext>
  )
}

Task Creation Form

'use client'

import { useState } from 'react'

export function CreateTaskForm({ onCreated }: { onCreated: () => void }) {
  const [title, setTitle] = useState('')
  const [priority, setPriority] = useState('medium')

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    await fetch('/api/tasks', {
      method: 'POST',
      body: JSON.stringify({ title, priority }),
    })
    setTitle('')
    onCreated()
  }

  return (
    <form onSubmit={handleSubmit} className="flex gap-3">
      <input
        type="text"
        value={title}
        onChange={e => setTitle(e.target.value)}
        placeholder="Task title..."
        className="flex-1 rounded-xl border px-4 py-2.5 text-sm"
        required
      />
      <select
        value={priority}
        onChange={e => setPriority(e.target.value)}
        className="rounded-xl border px-3 py-2.5 text-sm"
      >
        <option value="low">Low</option>
        <option value="medium">Medium</option>
        <option value="high">High</option>
      </select>
      <button
        type="submit"
        className="rounded-xl bg-brand-600 px-6 py-2.5 text-sm font-semibold text-white"
      >
        Add Task
      </button>
    </form>
  )
}

Build your project management SaaS with the Project Manager Pro template.

Browse all 200+ templates and starter kits.

Ready to Build?

Get started with our production-ready starter kits and ship your project faster.

Browse Starter Kits