Sextant
← Catalog
Block · people

Attrition Trend

Annualized attrition over a trailing year with the regrettable split and the year-over-year move.

State
Size
Mode

Team Attrition Trend

13.4%

trailing 12-mo · 59% regrettable

  • Attrition
  • Regrettable

Attrition grew +73.9% YoY to 13.4% — driven by regrettable exits.

Team Attrition Trend — monthly separations
MonthSeparationsHeadcountRegrettable
Apr 202522851
May 202522951
Jun 202523081
Jul 202533221
Aug 202533371
Sep 202533521
Oct 202533681
Nov 202523851
Dec 202524021
Jan 202674105
Feb 202684156
Mar 202674185
Apr 202664214
May 202654243

People ops data · trailing 12-month annualized rate

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

How it computes

Monthly annualized separation rate with a regrettable-attrition overlay and a trailing-twelve-month average.

annualized(m) = 12·separations / headcount;  TTM = Σ₁₂ separations / mean₁₂ headcount

Assumptions

  • Headcount is a point-in-time snapshot, stable within the month.
  • Regrettable separations are a subset of total separations.
  • Twelve months is the trailing window.

Honest about

  • Months with non-positive headcount annualize to 0 rather than blowing up.
  • TTM divides by the mean headcount over the window, not a single month-end balance — so a hiring spike doesn't deflate the rate.
  • Year-over-year delta needs both the latest and the 12-months-prior point; it is skipped rather than faked when history is short.

Use it with your data

Data contract

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

FieldTypeRequired
monthstringyes
headcountnumberyes
separationsnumberyes
regrettablenumberyes

Static props

import { PeopleAttritionTrend } from "@/components/blocks/people-attrition-trend/people-attrition-trend";

<PeopleAttritionTrend data={myData} />

Client fetch (SWR)

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

Server component (RSC)

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

<PeopleAttritionTrend data={data} />

Map arbitrary rows

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

const out = mapRows(rows, { /* contractKey: "yourColumn" */ }, dataSchema);
if (out.ok) <PeopleAttritionTrend 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 (attrition, regrettable)

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

Format numbers (currency / locale)

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

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

Replace or hide the narrative

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

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

Restyle any region

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

Drop the card chrome (headless plot)

import { PeopleAttritionTrendPlot } from "@/components/blocks/people-attrition-trend/people-attrition-trend";

// Just the chart body — bring your own card/layout:
<PeopleAttritionTrendPlot data={data} config={config} />