Europe/Kyiv
Projects

My Todo App — Full-Stack Task Manager with Drag-and-Drop

May 12, 2026
My Todo App
My Todo App is a full-stack task management application built with the Next.js App Router. It persists tasks in a PostgreSQL database via Prisma and supports drag-and-drop reordering out of the box. 🔗 my-todo-app-sable.vercel.app
🔗 check the GitHub repository for source code. Frontend & Backend are unified under a single Next.js application:
  • Next.js 16 + React 19 — App Router with Server Actions
  • Prisma 7 — ORM for PostgreSQL with custom output path and multi-file schemas
  • @prisma/adapter-pg — native pg driver adapter for Prisma
  • Neon — serverless PostgreSQL hosted via Vercel integration
  • @hello-pangea/dnd — accessible drag-and-drop for task reordering
  • Tailwind CSS 4 — utility-first styling
  • next-themes — dark / light mode switching
Tasks are stored in a serverless PostgreSQL database on Neon, provisioned via the Vercel integration and accessed through Prisma. The Next.js App Router handles both UI and data via Server Actions — no separate backend process required. New todos are prepended to the top by assigning an order value lower than the current minimum. Drag-and-drop reordering persists through a batched $transaction that updates every affected row in one round-trip.
// prisma/models/todo.prisma
model Todo {
  id        String   @id @default(cuid())
  text      String
  completed Boolean  @default(false)
  order     Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
// actions/todo.ts
export async function addTodo(formData: FormData) {
  const text = formData.get('text') as string;
  if (!text || text.trim() === '') return;

  const minOrderTodo = await prisma.todo.findFirst({
    orderBy: { order: 'asc' },
    select: { order: true },
  });

  const newOrder = minOrderTodo ? minOrderTodo.order - 1 : 0;

  await prisma.todo.create({ data: { text, order: newOrder } });
  revalidatePath('/');
}

export async function reorderTodos(ids: string[]) {
  const transactions = ids.map((id, index) =>
    prisma.todo.update({ where: { id }, data: { order: index } })
  );
  await prisma.$transaction(transactions);
  revalidatePath('/');
}
// components/todo-list.tsx
const onDragEnd = async (result: DropResult) => {
  if (!result.destination) return;

  const items = Array.from(todos);
  const [reorderedItem] = items.splice(result.source.index, 1);
  items.splice(result.destination.index, 0, reorderedItem);

  setTodos(items); // optimistic update
  await reorderTodos(items.map((i) => i.id));
};
npm install

# set DATABASE_URL in .env (get it from Neon dashboard or Vercel integration)
npx prisma migrate dev

npm run dev
This project shows how Next.js 16 with the App Router and Server Actions can replace a dedicated backend for straightforward CRUD applications. Prisma with the native pg adapter keeps the database layer clean and type-safe, while @hello-pangea/dnd adds a polished UX touch — with optimistic UI updates and a single batched transaction for persistence.