ibcs-react

Conformance

checkIbcs is the library's own IBCS-notation linter - lint a chart, KPI or report config in CI and get findings by rule id and JSON path.

Most chart libraries only let you draw. This one can also check a config against the IBCS notation rules it implements and tell you where the config departs from them - before it ships.

A notation linter

The same JSON config that renders a chart, KPI or report can also be linted. Call checkIbcs(config) and you get back an array of IbcsFinding objects: non-linear chart types, unstructured titles, missing data, wrong favorability for cost measures, and so on. An empty array means the config passes every rule the linter implements. It is pure logic - zero React, zero dependencies, JSON in and JSON out - so it runs in a unit test, a CI gate or an authoring tool.

import { checkIbcs, ConformanceReport } from "ibcs-react";

// A config that breaks the rules: pie isn't a linear chart type, the title is
// a bare string (not Who/What/When), and there's no data to plot.
const config = { type: "pie", title: "Revenue 2026", data: [] };

const findings = checkIbcs(config);
// → [
//     { rule: "linear-chart-type", severity: "error",   path: "type",  message: … },
//     { rule: "data-present",      severity: "error",   path: "data",  message: … },
//     { rule: "structured-title",  severity: "warning", path: "title", message: … },
//   ]
// An empty array means no rule was violated.

// Or render the findings - pass a target to lint on the fly…
<ConformanceReport target={config} />
// …or hand it findings you computed yourself:
<ConformanceReport findings={findings} />

The linter runs at build time, not as a house-style debate. A config either passes the IBCS notation rules the linter implements, or it lists exactly what to change - by rule id and JSON path.

Lint the JSX path

Most apps author charts as JSX, not JSON configs - the props are the same shapes minus the type discriminator, which the component name carries. checkIbcsProps maps the name back and runs the same rules, so a dashboard written the way getting started teaches is lintable in a unit test without restructuring:

import { checkIbcsProps } from "ibcs-react";

// <VarianceColumnChart data={productLines} comparison="PY" variance="abs" />
test("the revenue chart follows IBCS notation", () => {
  expect(
    checkIbcsProps("VarianceColumnChart", {
      data: productLines,
      comparison: "PY",
      variance: "abs",
      title: { who: "ACME", what: "Revenue (€k)", when: "2026" },
    }),
  ).toEqual([]);
});

Render-only props (width, tokens, onSelect, …) carry no rules and are ignored; lint-only declarations (measureKind) can ride along. KpiCard props already are a KpiConfig and lint directly. The specialised variance charts lint as the linear family they render - and checkIbcsProps("PieChart", …) flags the pie, exactly as it should.

Live playground

The config below deliberately breaks three rules: a pie (a non-linear chart type), a bare string title instead of a structured Who/What/When, and an empty data array. Edit the JSON and watch the findings update live - the same ConformanceReport component renders the result.

Findings
2 errors1 warning
  • errorchart type "pie" is non-linear - IBCS forbids area/angle encodings because they distort comparison. Use one of "varianceColumn", "trend", "structure", "waterfall", "stacked", "line", "area", "scatter", "bubble", "combo", "tree". (type) · linear-chart-type
  • errorchart has no data - nothing to plot. (data) · data-present
  • warningtitle "Revenue 2026" is a bare string - use a Who/What/When structured title (ISO 24896 SAY). (title) · structured-title

Out of the box this example produces two errors (non-linear chart type, no data) and one warning (bare-string title). Switch "pie" to a linear type such as "varianceColumn", give it a data row and replace the title with a structured { who, what, when } object - the list empties and the report says so. Deleting the title is not a way out: a chart or report with no title is flagged at the same severity (ISO 24896 SAY requires one), and an unknown type string gets a did-you-mean plus the list of valid values.

The rule catalog

Every finding references a rule id from IBCS_RULES, a serializable catalog you can surface in your own UI legends or docs. The full set the linter encodes:

Rule idSeverityWhat it checks
linear-chart-typeerror
Linear chart types only
IBCS permits only linear charts (column, bar, line, area, scatter, bubble, waterfall families). Pie, gauge, radar and similar are forbidden because area/angle encodings distort comparison. Unknown type strings are flagged under this rule too, with the valid values listed.
structured-titlewarning
Structured Who / What / When title
ISO 24896 SAY: a title states Who (entity), What (measure + unit) and When (period) on separate lines, kept apart from the interpretive key message. A bare string title loses that structure - and a chart or report with NO title at all says nothing. Both are flagged.
show-varianceinfo
Show a variance
IBCS reports compare actuals against a scenario (AC vs PY / PL / FC). A chart or KPI with no comparison shows a number without a yardstick.
data-presenterror
Data must be present
A chart or KPI with no data cannot be rendered or checked.
block-typeerror
Known report block type
Report blocks must be one of kpi, chart, statement, table or text.
cost-favorabilitywarning
Correct favorability for cost measures
For cost / expense / tax measures a higher value is unfavorable. Set higherIsBetter:false so impact (favorability) coloring shows overruns in red, not green. Detected from an explicit measureKind:"cost" declaration, or heuristically from the title / KPI label.
shared-scaleinfo
Consistent scaling across same-unit charts
Charts of the same unit should share a value scale (and a zero baseline) so bar lengths are comparable across the report.
ratio-unitsinfo
Percentage-point deltas for ratio measures
A percentage MEASURE (margin, rate, share) moves in percentage points, not percent-of-percent. A KPI formatted with suffix "%" should declare unit:"ratio" so its delta renders as +0.6pp instead of a misleading relative +0.9%.
input-shapeinfo
Recognizable config shape
The linter inspects ChartConfig, KpiConfig and ReportConfig shapes. Other values can't be checked.
import { IBCS_RULES } from "ibcs-react";

IBCS_RULES.map((rule) => `${rule.id} (${rule.severity}) - ${rule.title}`);

Declare what the measure is

The cost-favorability rule normally detects cost measures from the title or KPI label ("Operating expenses", "Tax", …) - structured titles included. When the wording doesn't give it away, declare it: measureKind: "cost" on a chart or KPI config makes the linter insist on higherIsBetter: false no matter what the title says, and measureKind: "revenue" silences the heuristic for measures that merely sound like costs ("Cost recovery revenue"). The declaration is linter-only - rendering still follows higherIsBetter.

checkIbcs({ type: "varianceColumn", measureKind: "cost", data, title });
// → cost-favorability warning until the config sets higherIsBetter: false

What a finding looks like

Each IbcsFinding carries a rule id, a severity ("error", "warning" or "info"), a human-readable message and an optional path locating the offending value (for example blocks[2].config.type).

FieldTypeMeaning
rulestringRule id - matches an entry in IBCS_RULES.
severity"error" | "warning" | "info"Errors mark a broken rule; warnings and info are advisory.
messagestringHuman-readable description of the departure.
pathstring?JSON-ish path to the offending value, e.g. blocks[2].title.

Scope

checkIbcs is the library's own check against the IBCS notation rules listed above, implemented from the publicly described notation. It is not a certification, an audit or an assessment by IBCS or ISO, and a clean run says nothing beyond "none of these rules were violated". Rules the linter does not implement - and anything about the correctness of your numbers - remain your call.

Pair it with rendering

The linter works on the very same configs the components render, so you can lint in CI and render in the app from one source of truth.

import { checkIbcs } from "ibcs-react";

test("dashboard configs break no notation rule", () => {
  for (const config of dashboardConfigs) {
    const errors = checkIbcs(config).filter((f) => f.severity === "error");
    expect(errors).toEqual([]);
  }
});
ibcs-react is an independent open-source library and is not affiliated with, certified by, or endorsed by the IBCS Association or ISO. It follows the IBCS® notation rules (the basis of ISO 24896); IBCS® is a registered trademark of the IBCS Association.

On this page