Skip to main content

compositionBar Renderer

renderAs: 'compositionBar' renders proportional segmented bars with an optional inline legend. Use it for part-to-whole distributions — portfolio mixes, resource splits, category breakdowns — displayed compactly inside a table cell or card field.

Core Examples​

Pattern 1: Row Data Contains Full Segments​

Each row provides its own segment objects with labels, values, and colors. Config controls only presentation.

Each row supplies its own segment objects. Config only sets legend and presentation options.

Portfolio
Composition
Operating Reserve
Cash52 (52.0%)
Bonds33 (33.0%)
Other15 (15.0%)
Growth Fund
Equity68 (68.0%)
Bonds20 (20.0%)
Cash12 (12.0%)
// Data shape — segments live in the row:
{ portfolio: 'Operating Reserve', mix: { segments: [
{ label: 'Cash', value: 52, color: '#22c55e' },
{ label: 'Bonds', value: 33, color: '#3b82f6' },
{ label: 'Other', value: 15, color: '#a855f7' },
]}}

// Field config — no segments option needed:
{ key: 'mix', renderAs: 'compositionBar', renderAsOptions: { legend: 'inline', percentages: true } }

Pattern 2: Row Values + Config Mapping​

Each row stores only numeric values. A segments function in config maps those into shared labels and colors — best when all rows share the same segment schema but values differ.

Rows store plain value arrays. Config segments function applies shared labels and colors.

Team
Allocation
North America
Cash48 (48.0%)
Equity37 (37.0%)
Debt15 (15.0%)
EMEA
Cash31 (31.0%)
Equity52 (52.0%)
Debt17 (17.0%)
APAC
Cash55 (55.0%)
Equity28 (28.0%)
Debt17 (17.0%)
{ key: 'values', renderAs: 'compositionBar', renderAsOptions: {
legend: 'inline',
segments: (entity) => {
const v = Array.isArray(entity.values) ? entity.values : [];
return [
{ label: 'Cash', value: Number(v[0] ?? 0), color: '#22c55e' },
{ label: 'Equity', value: Number(v[1] ?? 0), color: '#3b82f6' },
{ label: 'Debt', value: Number(v[2] ?? 0), color: '#ef4444' },
];
},
}}

Pattern 3: Static Reference Composition​

segments is a static array in config. Every row displays the same composition — useful for target mixes, policy baselines, or benchmark overlays.

Every row renders the same benchmark composition from config. Row data is not the segment source.

Account
Target Mix
Ops Budget
Cash40
Equity45
Debt15
Reserve Fund
Cash40
Equity45
Debt15
{ key: 'account', renderAs: 'compositionBar', renderAsOptions: {
legend: 'inline',
segments: [
{ label: 'Cash', value: 40, color: '#22c55e' },
{ label: 'Equity', value: 45, color: '#3b82f6' },
{ label: 'Debt', value: 15, color: '#ef4444' },
],
}}

Which Pattern Should I Use?​

PatternSegment values come fromLabels/colors come fromBest when
Row full segmentsRow data (fieldValue.segments)Same row objectsEvery row has a distinct composition
Row values + config mappingRow data (numeric array or fields)Config segments functionAll rows share the same schema, values differ
Static referenceConfig onlyConfig onlySame reference mix shown for every row

Segment Input Resolution​

The renderer resolves the segment source in this exact order:

  1. renderAsOptions.segments — if provided (static array or (entity) => segment[])
  2. fieldValue.segments — if the field value is an object with a segments array
  3. fieldValue directly — numeric array or object array

Once sourced, each segment is normalized:

  • Value: looked up as value → amount → total on the object; plain numbers used directly
  • Label: looked up as label → name → key → Segment N
  • Color: segment.color → segmentColors[index] → palette[index] → built-in palette

Segments with zero, negative, or non-numeric values are filtered out. If no valid segments remain, the renderer outputs Invalid.

Options Reference​

OptionTypeDefaultEffect
segmentsCompositionBarSegment[] | (entity) => segment[]field valueExplicit segment source. When provided, overrides field value segment resolution.
legend'inline' | 'none''inline''inline' renders a legend row per segment below the bar. 'none' hides it.
percentagesbooleantrueShow per-segment percentage in the inline legend. Does not affect segment widths.
totalsbooleanfalseRender a total row below the legend.
totalnumber | (entity) => numberderived sumOverride the denominator for percent calculations. Falls back to sum of valid segment values if not positive.
style'segmented' | 'continuous' | (entity) => ...'segmented''segmented' adds gaps between segments; 'continuous' renders one uninterrupted band.
palettestring[]6-color built-inFallback color palette applied by segment index.
segmentColorsstring[]unsetAlias for palette. Takes precedence over palette when both are set.
barHeightnumber | string'12px'Bar height. Numbers are treated as px.
segmentGapnumber | string'2px'Gap between segments in segmented style. Numbers are treated as px.
cornerRadiusnumber | string'999px'Corner radius applied to track and/or segment elements per cornerScope.
cornerScope'track' | 'segment' | 'both' | 'none' | (entity) => ...'segment'Where corner rounding is applied. 'segment' rounds individual segments; 'track' rounds the outer bar.
gapnumber | string'0.4rem'Gap between the bar and the legend block.
classNamestringunsetAdditional CSS class on the root element.

CompositionBarSegment shape​

{
label?: string; // display name (fallback: 'Segment N')
value: number | string; // magnitude — parsed to a number; zero/negative/invalid are filtered out
color?: string; // explicit segment color (overrides palette)
}

Built-in default palette​

['#4d94ff', '#35d4a1', '#f6ad55', '#b794f4', '#f687b3', '#63b3ed']

Applied by index when no explicit segment.color, segmentColors, or palette is provided.

Style and Corner Controls​

style: 'segmented' (default) renders visible gaps between segments, each with independent corner rounding. style: 'continuous' renders one uninterrupted band — corner rounding is applied to the first and last segments only.

cornerScope determines where cornerRadius is applied:

ValueEffect
'segment'Rounds individual segment blocks (default)
'track'Rounds the outer bar container
'both'Rounds both
'none'No rounding anywhere

Recipes​

Compact strip — no legend​

{ key: 'usage', renderAs: 'compositionBar', renderAsOptions: {
legend: 'none',
barHeight: 8,
gap: '0.25rem',
segments: (entity) => [
{ label: 'Focus', value: entity.focusHours, color: '#4d94ff' },
{ label: 'Messages', value: entity.messageHours, color: '#35d4a1' },
{ label: 'Media', value: entity.mediaHours, color: '#f6ad55' },
],
}}

Summary card with totals row​

{ key: 'capitalMix', renderAs: 'compositionBar', renderAsOptions: {
legend: 'inline',
percentages: true,
totals: true,
segments: (entity) => [
{ label: 'Cash', value: entity.cash, color: '#35d4a1' },
{ label: 'Equities', value: entity.equities, color: '#4d94ff' },
{ label: 'Liabilities', value: entity.liabilities, color: '#f56565' },
],
total: (entity) => entity.cash + entity.equities + entity.liabilities,
}}

Continuous bar (no gaps)​

{ key: 'mix', renderAs: 'compositionBar', renderAsOptions: {
style: 'continuous',
cornerScope: 'track',
cornerRadius: '4px',
legend: 'none',
segments: (entity) => entity.mixSegments,
}}

Caveats​

  • Segments with zero, negative, or non-parseable values are silently filtered out. If all segments are filtered, the renderer outputs the plain text Invalid.
  • percentages: true only affects legend display text — it does not change segment widths.
  • total override is only used when it resolves to a positive number. If zero or negative, the derived sum is used instead.
  • segmentColors and palette are aliases — segmentColors takes precedence when both are set.
  • segmentGap has no effect when style: 'continuous'.
  • In continuous style, cornerScope: 'segment' only rounds the first and last segments.
  • Segment tooltips (visible on hover) always include percentages regardless of the percentages option.

See Also​