Sextant
← Catalog
Block · people

eNPS

Employee NPS on a −100→+100 gauge with the promoter/passive/detractor split and the move since the last survey.

State
Size
Mode

Employee NPS · Q2 2026

+57

eNPS · 287 responses

  • Promoters
  • Passives
  • Detractors

eNPS climbed to +57 (+35 pts since last survey) — 65% promoters vs 8% detractors.

People team · Q2 survey closed May 30 · 287 responses · 65% participation

Demo data is illustrative. Replace with your own typed data prop.

How it computes

Employee Net Promoter Score: %promoters − %detractors, on a −100…+100 scale.

score = round(100·promoters/total − 100·detractors/total),  total = promoters + passives + detractors

Assumptions

  • Each respondent is exactly one of promoter / passive / detractor.
  • Passives count toward the base but not the score.

Honest about

  • Empty surveys degrade to 0 across the board rather than dividing by zero.
  • The score is rounded to an integer before both display and the prior-period delta, so the delta can't show phantom sub-point drift.
  • The gauge's zero midpoint is a fixed visual reference at 50% of the track, not a computed value.

Use it with your data

Data contract

Passed via the data prop — an object: validated at runtime, so a bad shape degrades to the error state rather than crashing.

FieldTypeRequired
promotersnumberyes
passivesnumberyes
detractorsnumberyes
priorScorenumberyes

Static props

import { PeopleEnps } from "@/components/blocks/people-enps/people-enps";

<PeopleEnps data={myData} />

Client fetch (SWR)

const { data, isLoading, error } = useSWR("/api/metric", fetcher);
// route isLoading/error through <ChartStates>, then:
<PeopleEnps data={data} />

Server component (RSC)

const data = await fetch(url, {
  next: { revalidate: 3600 },
}).then((r) => r.json());

<PeopleEnps data={data} />

Map arbitrary rows

import { mapRows } from "@/lib/sextant/column-map";

const out = mapRows(rows, { /* contractKey: "yourColumn" */ }, dataSchema);
if (out.ok) <PeopleEnps data={out.data} />;

Make it yours

Every customization below is an optional prop — omit them all and you get the defaults shown above. Recolor, reformat, restyle, or drop the card chrome without forking the component.

Recolor or rename a series (promoters, passives, detractors)

<PeopleEnps
  series={{ promoters: { color: "var(--chart-3)", label: "…" } }}
/>

Format numbers (currency / locale)

const eur = new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR",
  notation: "compact",
});

<PeopleEnps valueFormatter={(n) => eur.format(n)} />

Replace or hide the narrative

<PeopleEnps narrative="Your own one-liner." />

// …or hide it entirely:
<PeopleEnps narrative={false} />

Restyle any region

<PeopleEnps
  className="max-w-xl"
  classNames={{ body: "bg-muted/20" }}
/>