Skip to content
Framework: Vite + React + TypeScriptDifficulty: beginner

Todo, kanban and notes

A productivity app with a todo list, a drag-and-drop kanban board, and a notes editor sharing one local dataset.

Three views over persisted local state: a classic todo list with completion and filtering, a three-column board with HTML5 drag-and-drop plus keyboard-friendly move buttons, and a two-pane notes editor. Everything is typed and saved to localStorage through a tiny hook, which makes it a good base for learning state management before adding a database.

  • react
  • todo
  • kanban
  • notes
  • productivity
  • tailwind

This preview is the template's actual file tree and source code — exactly what is copied into a new project. Codexa does not publish screenshots or mock-ups of templates; run the project to see it rendered.

Files14 files

src/views/Notes.tsx66 lines
import { useState } from "react";
import { newId, type Note } from "../types";

type Props = { notes: Note[]; setNotes: (next: Note[] | ((prev: Note[]) => Note[])) => void };

export function Notes({ notes, setNotes }: Props) {
  const [selected, setSelected] = useState<string | null>(notes[0]?.id ?? null);
  const current = notes.find((n) => n.id === selected) ?? null;

  const create = () => {
    const n: Note = { id: newId(), title: "Untitled", body: "", updatedAt: Date.now() };
    setNotes((prev) => [n, ...prev]);
    setSelected(n.id);
  };
  const update = (patch: Partial<Note>) => {
    if (!current) return;
    setNotes((prev) => prev.map((n) => (n.id === current.id ? { ...n, ...patch, updatedAt: Date.now() } : n)));
  };
  const remove = () => {
    if (!current) return;
    setNotes((prev) => prev.filter((n) => n.id !== current.id));
    setSelected(null);
  };

  return (
    <div className="grid gap-4 md:grid-cols-[240px_1fr]">
      <aside className="rounded-xl border border-slate-200 bg-white p-2">
        <button type="button" onClick={create} className="w-full rounded-md bg-slate-900 px-3 py-1.5 text-sm text-white">
          New note
        </button>
        <ul className="mt-2 space-y-1">
          {notes.map((n) => (
            <li key={n.id}>
              <button type="button" onClick={() => setSelected(n.id)} aria-current={n.id === selected ? "true" : undefined} className={"w-full truncate rounded-md px-2 py-1.5 text-left text-sm " + (n.id === selected ? "bg-slate-100 font-medium" : "hover:bg-slate-50")}>
                {n.title || "Untitled"}
              </button>
            </li>
          ))}
          {notes.length === 0 ? <li className="p-3 text-center text-xs text-slate-500">No notes yet</li> : null}
        </ul>
      </aside>
      <section className="rounded-xl border border-slate-200 bg-white p-4">
        {current ? (
          <>
            <label className="sr-only" htmlFor="note-title">
              Title
            </label>
            <input id="note-title" value={current.title} onChange={(e) => update({ title: e.target.value })} className="w-full border-0 text-lg font-semibold outline-none" />
            <label className="sr-only" htmlFor="note-body">
              Body
            </label>
            <textarea id="note-body" value={current.body} onChange={(e) => update({ body: e.target.value })} rows={14} placeholder="Write something…" className="mt-2 w-full resize-y rounded-md border border-slate-200 p-3 text-sm" />
            <div className="mt-2 flex items-center justify-between text-xs text-slate-500">
              <span>Saved {new Date(current.updatedAt).toLocaleTimeString()}</span>
              <button type="button" onClick={remove} className="text-red-600 hover:underline">
                Delete note
              </button>
            </div>
          </>
        ) : (
          <p className="p-6 text-center text-sm text-slate-500">Select a note or create a new one.</p>
        )}
      </section>
    </div>
  );
}

Dependencies

Documented from the template's package.json. The runtime has a prebuilt dependency set for Vite + React + TypeScript, so installs are fast.

PackageVersion
react^19.1.0
react-dom^19.1.0
vite^6.3.0
@vitejs/plugin-react^4.4.0
tailwindcss^4.1.0
@tailwindcss/vite^4.1.0
typescript^5.8.0

How it runs

Framework
Vite + React + TypeScript
Dev command
npm run dev
Build command
npm run build
Output directory
dist
Port
5173
Opens at
/
Difficulty
beginner

Projects run in an isolated sandbox with a live preview. Read the projects documentation for details on settings, snapshots and deployment.

View all
  • Framework: Vite + React + TypeScriptDifficulty: beginner
    Featured

    React starter

    A clean React + TypeScript + Tailwind single-page app. The default target for AI generation.

    • react
    • typescript
    • tailwind
    • vite
    • spa

    9 files

  • Framework: Node.js + ExpressDifficulty: intermediate

    Express REST API

    A TypeScript Express API with routing, validation, and an in-memory store you can swap for PostgreSQL.

    • node
    • express
    • api
    • typescript
    • backend

    5 files

  • Framework: Node.js + ExpressDifficulty: intermediate

    Structured Express API

    A notes REST API split into router, middleware, and store modules, with logging, error handling, and search.

    • node
    • express
    • api
    • rest
    • typescript

    8 files

Need the raw data? GET /api/v1/templates/vite-react-tasks returns this template with its files.

Build on Todo, kanban and notes

Create a free account, start from this template, and ask the AI agent to turn it into your app.