CRM Pipeline Page React block for Tailwind CSS v4
v1.0.1Updated in 1.9.0
A React CRM pipeline page: pipeline switcher, search and filters, a deals board or list with totals, a deal sheet and a new deal dialog.
Switch the pipeline from the title (B2B sales or Partnerships; a bottom sheet on phones): the filters reset, the view stays. Pick a close date range in the header (This month, Next month, This quarter, Next 90 days, or two months of days; clear it with its ×). Type in the search to find a deal by title, company or contact, press Mine, No activity 14+ days or Over $10,000, and Clear filters to reset; the new count is announced. The board's own toolbar switches Board and List, filters by owner chips and hides or shows the won and lost columns; its summary follows the filters. Drag a card to another stage (long press on touch, or focus it and use Space and the arrows), or use its ⋯ menu (Move to, Mark as won, Mark as lost, Open, Delete). A drop on Lost asks for the reason first. Moves save optimistically; a failed save springs the card back with a toast. + Add deal under a column adds one by title. Click, tap or press Enter on a card to open its sheet (from the right, a tall bottom sheet on phones): rename it inline, change the value, stage, close date and owner, read the contact and the details, write a note (⌘ Enter adds it) and read the activity; Save appears once something changed, Mark as won and Lost close it (the reason dialog requires a reason), Reopen brings a closed deal back, ⋯ deletes it with an undo toast. Closing the sheet with unsaved changes asks Discard your changes? first; Escape closes it and focus returns to the card. New deal (or + on phones) opens a validated form: title, a company combobox that can add a new company, its contacts, value, stage, close date (30 days out by default) and owner; the new deal lands in its column. Sections rise in once on mount; reduced motion keeps everything still.
CRM Pipeline Page is a Pro React block in the React blocks category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add crm-pipeline-page, together with Alert, Alert Dialog, Avatar, Badge, Button and 31 more and the npm packages motion, lucide-react, react-hook-form and zod.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add crm-pipeline-pageCRM Pipeline Page is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import CrmPipelinePage from @/components/blocks/crm-pipeline-page.
// app/(admin)/deals/page.tsx (the shell lives in app/(admin)/layout.tsx: <AdminShell layout="fill">{children}</AdminShell>)
"use client"
import { CrmPipelinePage, type CrmDeal } from "@/components/blocks/crm-pipeline-page"
export default function OportunitatiPage() {
return (
<CrmPipelinePage
height="fill"
currentUserId="ana-pop"
onDealMove={async (move) => {
await fetch(`/api/deals/${move.deal.id}`, {
method: "PATCH",
body: JSON.stringify({ stageId: move.to.id, lostReason: move.lostReason }),
})
}}
onDealCreate={async (deal: CrmDeal) => {
const response = await fetch("/api/deals", { method: "POST", body: JSON.stringify(deal) })
return (await response.json()) as CrmDeal
}}
/>
)
}Props
pipelinesCrmPipeline[]The pipelines ({ id, name, stages: DealStage[] }) of the title switcher; defaults to CRM_PIPELINE_SAMPLE.pipelines (B2B sales and Partnerships).
pipelinestringThe shown pipeline's id, controlled; pair it with onPipelineChange.
defaultPipelinedefault"b2b-sales"stringThe pipeline shown first while uncontrolled.
onPipelineChange(pipeline: string) => voidCalled when the title switcher picks another pipeline.
dealsCrmDeal[]Every pipeline's deals, controlled (DealData plus companyId, contactId, createdAt, source, products and activity); a deal belongs to the pipeline that has its stage.
defaultDealsCrmDeal[]The deals while uncontrolled; defaults to CRM_PIPELINE_SAMPLE.deals (the 12 W8 deals and 4 partnerships).
onDealsChange(deals: CrmDeal[]) => voidCalled with the whole new list after every move, edit, create and delete.
view"board" | "list"The board or the grouped list, controlled.
defaultViewdefault"board""board" | "list"The view while uncontrolled; switching pipelines keeps it.
onViewChange(view: DealPipelineView) => voidCalled when the board's Board / List switch changes.
owners{ id: string; name: string; avatar?: string; role?: string }[]The sales team for the owner pickers; defaults to CRM_PIPELINE_SAMPLE.owners.
companiesCrmPipelineCompany[]Clients and their contacts for the new deal form and the sheet's contact card; defaults to CRM_PIPELINE_SAMPLE.companies.
lostReasonsstring[]The reasons offered when a deal is lost; defaults to Price, Chose a competitor, Not enough budget, No decision, Other reason.
currentUserIddefault"ana-pop"stringWho you are: the Mine chip and the default owner of new deals.
ownerFilterstring[]The owner ids pressed in the board's owner chips (Mine presses yours), controlled.
defaultOwnerFilterstring[]The owner filter while uncontrolled; defaults to [] (everyone).
onOwnerFilterChange(owners: string[]) => voidCalled when an owner chip or Mine changes the filter.
closePeriodDateRange | nullThe expected close date filter, controlled; null shows every date.
defaultClosePeriodDateRange | nullThe close date filter while uncontrolled; defaults to null.
onClosePeriodChange(period: DateRange | null) => voidCalled when the header's date range is picked or cleared.
largeDealAmountdefault10000numberThe threshold of the Over $10,000 chip, in the page currency.
heightdefault"auto""auto" | "fill" | "screen"fill takes what is left of a shell with layout="fill" and scrolls the board inside; screen is a standalone h-dvh page; auto grows with the longest column. Phones scroll the page around a tall board.
showSummarydefaulttruebooleanThe board's summary tiles (open pipeline, weighted forecast, won this month, win rate, average deal).
defaultShowCloseddefaulttruebooleanWhether the Won and Lost columns start visible (the board's Show closed switch).
staleAfterDaysdefault14numberDays in a stage before a card shows the stale hint, and the No activity chip's threshold; 0 turns both off.
requireLostReasondefaulttruebooleanA move to Pierdut (drag, menu, sheet) asks for a reason and blocks until one is chosen.
defaultOpenDealdefault""stringA deal id whose sheet opens on mount.
onDealMove(move: DealMove) => (boolean | void | Promise<unknown>)A drop, a menu move or a sheet move. false refuses it, a promise keeps the card saving and a rejection springs it back with a toast. Without it a 600 ms request is simulated.
onDealSave(deal: CrmDeal) => (void | Promise<unknown>)The sheet's saves (title, value, close date, owner, a note), optimistic; a rejection rolls back with a toast. Simulated by default.
onDealCreate(deal: CrmDeal) => (void | CrmDeal | Promise<void | CrmDeal>)A deal from the dialog or a column's quick add; resolve with the stored deal to replace the draft. A rejection shows the error in the dialog. Simulated by default.
onDealDelete(deal: CrmDeal) => (void | Promise<unknown>)Deleting from a card or the sheet, after a confirm; an undo toast follows, a rejection restores it. Simulated by default.
labelsPartial<CrmPipelinePageLabels>Every visible string (English by default, DEFAULT_CRM_PIPELINE_PAGE_LABELS); CRM_PIPELINE_PAGE_LABELS_RO is the ready Romanian set. Nested keys (pipeline, dateRange, datePicker, datePresets, currencyInput, inlineEdit, select, contactCard, timeline, validation) translate the composed items.
localedefault"en-US"stringIntl locale for money, numbers and dates.
currencydefault"USD"stringThe pipeline currency; deals in another currency are counted apart, never converted.
nowDate | numberThe reference time for stale hints, overdue dates and new stamps; defaults to the live clock.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"The accent of the primary actions, the pressed chips, the probability bars and the win rate ring.
radiusdefault"md""sm" | "md" | "lg"The summary tiles, the empty state, the sheet and the dialogs (12px, 16px or 24px tiles).
loadingdefaultfalsebooleanThe header, the summary and the columns as skeletons.
animateIndefaulttruebooleanThe header and the board rise in once on mount; off in thumbnails and under reduced motion.
linkComponentdefault"<a>"(props: LinkComponentProps) => ReactNodeThe router link for the company and deal links; defaults to the LinkProvider's (a plain anchor).
classNamestringMerged onto the root.
classNamesPartial<Record<CrmPipelinePageSlot, string>>Classes for the parts: header, filters, board, sheet, newDialog, empty.
Parts
DEFAULT_CRM_PIPELINE_PAGE_LABELS
constantEvery English string of the pipeline page: toolbar, board, list, deal sheet, new deal dialog and announcements. Spread it and override what you translate.
CRM_PIPELINE_PAGE_LABELS_EN
constantAn alias of DEFAULT_CRM_PIPELINE_PAGE_LABELS, kept so older imports keep working.
CRM_PIPELINE_PAGE_LABELS_RO
constantA ready Romanian set of every string. Pass it as labels for a Romanian CRM.
CRM_PIPELINE_SAMPLE
constantThe demo content in one object: pipelines, deals, owners, companies, lost reasons, sources, now and the current user. Replace it with data from your API.
CRM_PIPELINES
constantThe sample pipelines with their stages and win probabilities, for the pipeline switcher.
CRM_PIPELINE_OWNERS
constantThe sample sales team that owns the deals, with names and avatars.
CRM_PIPELINE_COMPANIES
constantThe sample client companies the deals belong to, offered in the new deal dialog.
CRM_LOST_REASONS
constantThe reasons offered when a deal is marked lost: price, a competitor, budget, no decision and other.
CRM_DEAL_SOURCES
constantThe lead sources offered for a deal: referral, website, event, cold call, existing client and partner.
CRM_PIPELINE_NOW
constantThe fixed "now" of the sample (September 24, 2026, 4:40 PM) that close dates are measured from.
CRM_PIPELINE_DEAL_IDS
constantThe four sample deal ids the demo offers for defaultOpenDeal.
Examples
Deal sheet open on ERP integration
defaultOpenDeal="d02": Construct Group, $103,400 in Negotiation, with its contact and activity.
List view
defaultView="list": a table grouped by stage with totals; cards on phones.
Only my deals
The Mine chip pressed, closed columns hidden: Needs analysis and Negotiation are empty.
The Partnerships pipeline
defaultPipeline="partnerships": three stages, four deals, no lost column.
Brand tone, large radius
tone="brand" on the primary actions, the chips and the probability bars; radius="lg".
Loading
loading: the header, the summary and the columns as skeletons.
Romanian copy, RON
CRM_PIPELINE_PAGE_LABELS_RO, locale="ro-RO", currency="RON": the ready Romanian set for the page and its parts.
Dependencies
Files
- components/blocks/crm-pipeline-page.tsx
- components/blocks/crm-pipeline-page-data.ts
FAQ
Is the React CRM Pipeline Page block free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install CRM Pipeline Page?
Run npx wingo-ui@latest add crm-pipeline-page. It also adds the Wingo UI items it builds on (Alert, Alert Dialog, Avatar, Badge, Button and 31 more) and the npm packages motion, lucide-react, react-hook-form and zod. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/crm-pipeline-page.
How does CRM Pipeline Page behave on phones?
Switch the pipeline from the title (B2B sales or Partnerships; a bottom sheet on phones): the filters reset, the view stays.
See also
More React blocks
Articles using this component
- React CRM Template: Pipeline, Forecast, Company Pages
Build it: screens and apps11 min read