CRM pipeline
Contacts table plus a five-stage deal pipeline board with inline stage changes, all persisted locally.
A small CRM with two views over the same data: a searchable contacts table with stage selects and delete, and a kanban-style pipeline (Lead → Qualified → Proposal → Won/Lost) where deals move between columns with keyboard-accessible buttons. State lives in localStorage via a typed hook so the app works offline; swap src/store.ts for API calls when you add a backend.
- react
- crm
- pipeline
- kanban
- 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
import { STAGES, type Stage, type useContacts } from "./store";
export function Pipeline({ store }: { store: ReturnType<typeof useContacts> }) {
const move = (id: string, dir: -1 | 1, current: Stage) => {
const i = STAGES.indexOf(current);
const next = STAGES[i + dir];
if (next) store.update(id, { stage: next });
};
return (
<div className="grid gap-3 md:grid-cols-5">
{STAGES.map((stage) => {
const items = store.contacts.filter((c) => c.stage === stage);
return (
<section key={stage} aria-labelledby={"stage-" + stage} className="rounded-xl border border-slate-200 bg-white p-3">
<h2 id={"stage-" + stage} className="flex items-center justify-between text-sm font-medium">
{stage}
<span className="rounded-full bg-slate-100 px-2 text-xs text-slate-600">{items.length}</span>
</h2>
<ul className="mt-3 space-y-2">
{items.map((c) => (
<li key={c.id} className="rounded-lg border border-slate-200 p-3 text-sm">
<p className="font-medium">{c.company}</p>
<p className="text-slate-600">{c.name}</p>
<p className="mt-1 text-xs text-slate-500">{"$" + c.value.toLocaleString()}</p>
<div className="mt-2 flex justify-between">
<button type="button" onClick={() => move(c.id, -1, c.stage)} disabled={stage === STAGES[0]} className="rounded border border-slate-300 px-2 py-0.5 text-xs disabled:opacity-40" aria-label={"Move " + c.company + " back"}>
←
</button>
<button type="button" onClick={() => move(c.id, 1, c.stage)} disabled={stage === STAGES[STAGES.length - 1]} className="rounded border border-slate-300 px-2 py-0.5 text-xs disabled:opacity-40" aria-label={"Move " + c.company + " forward"}>
→
</button>
</div>
</li>
))}
{items.length === 0 ? <li className="rounded-lg border border-dashed border-slate-200 p-3 text-center text-xs text-slate-400">Empty</li> : null}
</ul>
</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.
| Package | Version |
|---|---|
| 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
- intermediate
Projects run in an isolated sandbox with a live preview. Read the projects documentation for details on settings, snapshots and deployment.
Need the raw data? GET /api/v1/templates/vite-react-crm returns this template with its files.
Build on CRM pipeline
Create a free account, start from this template, and ask the AI agent to turn it into your app.