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 { useState } from "react";
import { useContacts } from "./store";
import { ContactsTable } from "./ContactsTable";
import { Pipeline } from "./Pipeline";
import { ContactForm } from "./ContactForm";
export default function App() {
const store = useContacts();
const [tab, setTab] = useState<"contacts" | "pipeline">("pipeline");
const [adding, setAdding] = useState(false);
const open = store.contacts.filter((c) => c.stage !== "Won" && c.stage !== "Lost");
const pipelineValue = open.reduce((s, c) => s + c.value, 0);
return (
<div className="min-h-screen bg-slate-50 text-slate-900">
<header className="border-b border-slate-200 bg-white">
<div className="mx-auto flex max-w-6xl flex-wrap items-center justify-between gap-3 px-4 py-3 sm:px-6">
<h1 className="font-semibold tracking-tight">Pipeline CRM</h1>
<div className="flex items-center gap-2">
<div className="flex rounded-md border border-slate-300 p-0.5 text-sm" role="tablist" aria-label="View">
{(["pipeline", "contacts"] as const).map((t) => (
<button key={t} type="button" role="tab" aria-selected={tab === t} onClick={() => setTab(t)} className={"rounded px-3 py-1 capitalize " + (tab === t ? "bg-slate-900 text-white" : "hover:bg-slate-100")}>
{t}
</button>
))}
</div>
<button type="button" onClick={() => setAdding(true)} className="rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-700">
New contact
</button>
</div>
</div>
</header>
<main className="mx-auto max-w-6xl px-4 py-6 sm:px-6">
<div className="grid gap-4 sm:grid-cols-3">
<Stat label="Contacts" value={String(store.contacts.length)} />
<Stat label="Open deals" value={String(open.length)} />
<Stat label="Pipeline value" value={"$" + pipelineValue.toLocaleString()} />
</div>
<div className="mt-6">{tab === "pipeline" ? <Pipeline store={store} /> : <ContactsTable store={store} />}</div>
{adding ? (
<ContactForm
onCancel={() => setAdding(false)}
onSave={(c) => {
store.add(c);
setAdding(false);
}}
/>
) : null}
</main>
</div>
);
}
function Stat({ label, value }: { label: string; value: string }) {
return (
<div className="rounded-xl border border-slate-200 bg-white p-4">
<p className="text-xs uppercase tracking-wide text-slate-500">{label}</p>
<p className="mt-1 text-2xl font-semibold">{value}</p>
</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.