Xzero UI
Pro library

Lead scoringPro

A lead scoring card where inbound B2B leads are ranked by an AI fit score, each with a score ring and a one-line reason. Open a lead to see its signals and score breakdown, then push it to HubSpot or Salesforce.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/lead-scoring
Live · keyboard ready

Pass your leads newest first, each with a weighted breakdown, so the ring and the bars always agree (or set score directly). Mark a lead scoring while your model works on it. Wire onPush(lead, crm, signal) to your backend: resolve for the done state, reject for the error and retry state. crms picks the destinations, and sort/onSortChange control the order. onRescore shows a rescore button. With no props it plays a short intro once it scrolls into view: scores land, the list re-sorts by score, the top lead opens and is pushed to HubSpot. Every push in the demo is simulated.

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/lead-scoring

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { LeadScoring } from "@/components/xzero/blocks/lead-scoring/lead-scoring";

export function InboundLeads({ leads }: { leads: Lead[] }) {
  return (
    <LeadScoring
      leads={leads}
      onPush={async (lead, crm, signal) => {
        const res = await fetch(`/api/crm/${crm}/leads`, { method: "POST", body: JSON.stringify({ id: lead.id }), signal });
        if (!res.ok) throw new Error("Push failed");
      }}
    />
  );
}

API reference

LeadScoring

A card of inbound leads ranked by an AI fit score. Each row shows the company mark, the person, a score ring and the model's one-line reason; opening a row shows signal chips, the weighted score breakdown and a push to HubSpot or Salesforce. With no props it plays a short intro on first view with sample leads and simulated pushes.

PropTypeDefaultDescription
leadsLead[]leadScoringSamplesLeads to show, newest first. Each lead has id, name, title, company, mark, received, reason, and optional signals, breakdown, score, scoring and pushedTo.
titlestring"Inbound leads"Card title.
descriptionReactNode–Line under the title. Defaults to the scoring model and the lead count.
sort"score" | "newest"–Controlled sort. Newest keeps the array order.
defaultSort"score" | "newest""newest" while auto playing, else "score"Starting sort when uncontrolled.
onSortChange(sort: LeadSort) => void–Called when the sort control changes.
crmsLeadCrmId[]["hubspot", "salesforce"]CRMs a lead can be pushed to. The first is the default destination; one entry hides the picker.
onPush(lead: Lead, crm: LeadCrmId, signal: AbortSignal) => Promise<void>–Creates the contact and deal. Resolve for the done state, reject for the error state with a retry. The signal aborts on unmount. Without it pushes are simulated and announced as such.
onRescore() => void–Shows a rescore button. Mark leads scoring while you rescore them. The sample demo rescores on its own.
autoPlaybooleantrue for sample dataPlays the intro once when the card scrolls into view: scores land, the list sorts by score, the top lead opens and is pushed to the first CRM.
defaultExpandedIdstring | nullnullLead open on first render.
classNamestring–Class on the card.

Lead

One inbound lead.

PropTypeDefaultDescription
id (required)string–Stable id, used as the React key and for push state.
name (required)string–Person's name.
title (required)string–Job title. Hidden in the row on narrow cards.
company (required)string–Company name.
markLeadCompanyMarkId | ReactNode–"vercel", "ramp", "linear", "notion", "retool", "figma", or your own 20px logo node. Falls back to a monogram.
received (required)string–Arrival time, already formatted ("6m ago").
reason (required)string–The model's one-line reason for the score.
signals{ kind: LeadSignalKind; label: string }[]–Chips in the open row. Kinds: pricing, team, hiring, email, demo, stack, docs, repo, security.
breakdown{ id: string; label: string; value: number; weight: number }[]–Score factors, 0 to 100, with weights that add up to 1. The score is their weighted sum unless score is set.
scorenumber–0 to 100. Overrides the breakdown sum.
scoringboolean–Still being scored: the ring spins and the reason shimmers.
pushedToLeadCrmId[]–CRMs the lead already lives in. Shows Synced in the row.

Keyboard interactions

KeysAction
TabMoves through the sort control, rescore, each lead row and the open row's controls.
Enter / SpaceOpens or closes the focused lead.
ArrowDown / ArrowUpMoves focus between lead rows.
Home / EndJumps to the first or last lead row.
ArrowLeft / ArrowRightSwitches the CRM destination inside the open row.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Guidance for AI tools

Blocks are complete, self-contained screens with sample data. Replace the sample data and connect the callbacks described above. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components