A poster of who is connected to whom: a full-width arc diagram with one arc per connection, as thick as its value (arc-diagram), a strip of two to four large figures, and a bottom row where the same entities return as circles joined by lines (bubble-network) and as a ranking of centred pills (pill-rank).
Arc diagram
Places entities in order on a line and joins each related pair with a semicircular arc whose thickness is the value of the connection; dots are sized by each entity's total.
- Use when
- Connections among up to about 20 entities that have a meaningful order (stops on a line, chapters, age groups, teams by size): trips between stations, trade between neighbours, characters sharing scenes.
- Avoid when
- Flows with a direction through stages (use sankey chart); one source splitting into parts (use fan flow); a full matrix of every pair where exact values matter (use heatmap); more than about 20 entities or 60 connections (use heatmap).
Preview
import { ArcDiagram } from "insightcharts";
const data = [
{ a: "Harbour", b: "Market", trips: 4200 },
{ a: "Harbour", b: "Quay", trips: 900 },
{ a: "Quay", b: "Market", trips: 1300 },
{ a: "Market", b: "Town Hall", trips: 2600 },
{ a: "Market", b: "University", trips: 3100 },
{ a: "Town Hall", b: "University", trips: 1500 },
{ a: "Town Hall", b: "Hospital", trips: 1100 },
{ a: "University", b: "Park", trips: 800 },
{ a: "University", b: "Airport", trips: 1900 },
{ a: "Park", b: "Hospital", trips: 600 },
{ a: "Hospital", b: "Stadium", trips: 700 },
{ a: "Stadium", b: "Airport", trips: 1200 },
{ a: "Harbour", b: "Airport", trips: 2300 },
{ a: "Market", b: "Stadium", trips: 1700 },
{ a: "Quay", b: "University", trips: 500 },
];
export function Example() {
return (
<ArcDiagram
data={data}
from="a"
to="b"
value="trips"
order={[
"Harbour",
"Quay",
"Market",
"Town Hall",
"University",
"Park",
"Hospital",
"Stadium",
"Airport",
]}
colorBy="none"
format="integer"
title="Harbour and Market stations exchanged 4,200 trips a day"
subtitle="Daily trips between pairs of stations on one tram line, both directions; dots show each station's total"
note="Illustrative figures for an invented line."
/>
);
}{
"type": "arc-diagram",
"data": [
{ "a": "Harbour", "b": "Market", "trips": 4200 },
{ "a": "Harbour", "b": "Quay", "trips": 900 },
{ "a": "Quay", "b": "Market", "trips": 1300 },
{ "a": "Market", "b": "Town Hall", "trips": 2600 },
{ "a": "Market", "b": "University", "trips": 3100 },
{ "a": "Town Hall", "b": "University", "trips": 1500 },
{ "a": "Town Hall", "b": "Hospital", "trips": 1100 },
{ "a": "University", "b": "Park", "trips": 800 },
{ "a": "University", "b": "Airport", "trips": 1900 },
{ "a": "Park", "b": "Hospital", "trips": 600 },
{ "a": "Hospital", "b": "Stadium", "trips": 700 },
{ "a": "Stadium", "b": "Airport", "trips": 1200 },
{ "a": "Harbour", "b": "Airport", "trips": 2300 },
{ "a": "Market", "b": "Stadium", "trips": 1700 },
{ "a": "Quay", "b": "University", "trips": 500 }
],
"from": "a",
"to": "b",
"value": "trips",
"order": [
"Harbour",
"Quay",
"Market",
"Town Hall",
"University",
"Park",
"Hospital",
"Stadium",
"Airport"
],
"colorBy": "none",
"format": "integer",
"title": "Harbour and Market stations exchanged 4,200 trips a day",
"subtitle": "Daily trips between pairs of stations on one tram line, both directions; dots show each station's total",
"note": "Illustrative figures for an invented line."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Used in
1 infographic uses arc diagram.
Infographics
1Installation
$ pnpm dlx shadcn@latest add @insightcharts/arc-diagramThe CLI copies the source into your project and needs the @insightcharts registry in components.json; see installation. With the package, import it by name:
import { ArcDiagram } from "insightcharts";Examples
One entity highlighted
import { ArcDiagram } from "insightcharts";
const data = [
{ a: "Harbour", b: "Market", trips: 4200 },
{ a: "Harbour", b: "Quay", trips: 900 },
{ a: "Quay", b: "Market", trips: 1300 },
{ a: "Market", b: "Town Hall", trips: 2600 },
{ a: "Market", b: "University", trips: 3100 },
{ a: "Town Hall", b: "University", trips: 1500 },
{ a: "Town Hall", b: "Hospital", trips: 1100 },
{ a: "University", b: "Park", trips: 800 },
{ a: "University", b: "Airport", trips: 1900 },
{ a: "Park", b: "Hospital", trips: 600 },
{ a: "Hospital", b: "Stadium", trips: 700 },
{ a: "Stadium", b: "Airport", trips: 1200 },
{ a: "Harbour", b: "Airport", trips: 2300 },
{ a: "Market", b: "Stadium", trips: 1700 },
{ a: "Quay", b: "University", trips: 500 },
];
export function Example() {
return (
<ArcDiagram
data={data}
from="a"
to="b"
value="trips"
order={[
"Harbour",
"Quay",
"Market",
"Town Hall",
"University",
"Park",
"Hospital",
"Stadium",
"Airport",
]}
highlight="University"
format="integer"
title="University station had 7,800 daily trips to and from 5 other stations"
subtitle="Daily trips between pairs of stations on one tram line, both directions"
note="Illustrative figures for an invented line."
/>
);
}{
"type": "arc-diagram",
"data": [
{ "a": "Harbour", "b": "Market", "trips": 4200 },
{ "a": "Harbour", "b": "Quay", "trips": 900 },
{ "a": "Quay", "b": "Market", "trips": 1300 },
{ "a": "Market", "b": "Town Hall", "trips": 2600 },
{ "a": "Market", "b": "University", "trips": 3100 },
{ "a": "Town Hall", "b": "University", "trips": 1500 },
{ "a": "Town Hall", "b": "Hospital", "trips": 1100 },
{ "a": "University", "b": "Park", "trips": 800 },
{ "a": "University", "b": "Airport", "trips": 1900 },
{ "a": "Park", "b": "Hospital", "trips": 600 },
{ "a": "Hospital", "b": "Stadium", "trips": 700 },
{ "a": "Stadium", "b": "Airport", "trips": 1200 },
{ "a": "Harbour", "b": "Airport", "trips": 2300 },
{ "a": "Market", "b": "Stadium", "trips": 1700 },
{ "a": "Quay", "b": "University", "trips": 500 }
],
"from": "a",
"to": "b",
"value": "trips",
"order": [
"Harbour",
"Quay",
"Market",
"Town Hall",
"University",
"Park",
"Hospital",
"Stadium",
"Airport"
],
"highlight": "University",
"format": "integer",
"title": "University station had 7,800 daily trips to and from 5 other stations",
"subtitle": "Daily trips between pairs of stations on one tram line, both directions",
"note": "Illustrative figures for an invented line."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Coloured by entity, counting rows
import { ArcDiagram } from "insightcharts";
const data = [
{ a: "Ana", b: "Ben" },
{ a: "Ana", b: "Ben" },
{ a: "Ana", b: "Ben" },
{ a: "Ana", b: "Cleo" },
{ a: "Ben", b: "Cleo" },
{ a: "Ben", b: "Cleo" },
{ a: "Cleo", b: "Dev" },
{ a: "Cleo", b: "Dev" },
{ a: "Cleo", b: "Dev" },
{ a: "Cleo", b: "Dev" },
{ a: "Dev", b: "Eli" },
{ a: "Ana", b: "Eli" },
{ a: "Ana", b: "Eli" },
{ a: "Ben", b: "Dev" },
{ a: "Eli", b: "Fay" },
{ a: "Eli", b: "Fay" },
{ a: "Dev", b: "Fay" },
];
export function Example() {
return (
<ArcDiagram
data={data}
from="a"
to="b"
title="Cleo and Dev shared 4 of the play's 17 two-person scenes"
subtitle="Scenes shared by pairs of six characters in one play, in order of first appearance"
note="Illustrative figures for an invented play."
/>
);
}{
"type": "arc-diagram",
"data": [
{ "a": "Ana", "b": "Ben" },
{ "a": "Ana", "b": "Ben" },
{ "a": "Ana", "b": "Ben" },
{ "a": "Ana", "b": "Cleo" },
{ "a": "Ben", "b": "Cleo" },
{ "a": "Ben", "b": "Cleo" },
{ "a": "Cleo", "b": "Dev" },
{ "a": "Cleo", "b": "Dev" },
{ "a": "Cleo", "b": "Dev" },
{ "a": "Cleo", "b": "Dev" },
{ "a": "Dev", "b": "Eli" },
{ "a": "Ana", "b": "Eli" },
{ "a": "Ana", "b": "Eli" },
{ "a": "Ben", "b": "Dev" },
{ "a": "Eli", "b": "Fay" },
{ "a": "Eli", "b": "Fay" },
{ "a": "Dev", "b": "Fay" }
],
"from": "a",
"to": "b",
"title": "Cleo and Dev shared 4 of the play's 17 two-person scenes",
"subtitle": "Scenes shared by pairs of six characters in one play, in order of first appearance",
"note": "Illustrative figures for an invented play."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Ten entities, one colour each
import { ArcDiagram } from "insightcharts";
const data = [
{ a: "Pier", b: "Docks", trips: 1800 },
{ a: "Pier", b: "Forum", trips: 2400 },
{ a: "Docks", b: "Canal", trips: 900 },
{ a: "Canal", b: "Forum", trips: 1500 },
{ a: "Forum", b: "Abbey", trips: 3300 },
{ a: "Forum", b: "Castle", trips: 1300 },
{ a: "Abbey", b: "Castle", trips: 1100 },
{ a: "Abbey", b: "Mills", trips: 700 },
{ a: "Castle", b: "Garden", trips: 1600 },
{ a: "Garden", b: "Mills", trips: 800 },
{ a: "Mills", b: "Heath", trips: 1000 },
{ a: "Heath", b: "Depot", trips: 1400 },
{ a: "Garden", b: "Depot", trips: 600 },
{ a: "Pier", b: "Depot", trips: 500 },
{ a: "Canal", b: "Heath", trips: 400 },
];
export function Example() {
return (
<ArcDiagram
data={data}
from="a"
to="b"
value="trips"
order={[
"Pier",
"Docks",
"Canal",
"Forum",
"Abbey",
"Castle",
"Garden",
"Mills",
"Heath",
"Depot",
]}
colorBy="series"
format="integer"
title="Forum and Abbey stations exchanged 3,300 trips a day"
subtitle="Daily trips between pairs of ten stations on one tram line, both directions; each arc takes the colour of the station nearer the start of the line"
note="Illustrative figures for an invented line."
/>
);
}{
"type": "arc-diagram",
"data": [
{ "a": "Pier", "b": "Docks", "trips": 1800 },
{ "a": "Pier", "b": "Forum", "trips": 2400 },
{ "a": "Docks", "b": "Canal", "trips": 900 },
{ "a": "Canal", "b": "Forum", "trips": 1500 },
{ "a": "Forum", "b": "Abbey", "trips": 3300 },
{ "a": "Forum", "b": "Castle", "trips": 1300 },
{ "a": "Abbey", "b": "Castle", "trips": 1100 },
{ "a": "Abbey", "b": "Mills", "trips": 700 },
{ "a": "Castle", "b": "Garden", "trips": 1600 },
{ "a": "Garden", "b": "Mills", "trips": 800 },
{ "a": "Mills", "b": "Heath", "trips": 1000 },
{ "a": "Heath", "b": "Depot", "trips": 1400 },
{ "a": "Garden", "b": "Depot", "trips": 600 },
{ "a": "Pier", "b": "Depot", "trips": 500 },
{ "a": "Canal", "b": "Heath", "trips": 400 }
],
"from": "a",
"to": "b",
"value": "trips",
"order": [
"Pier",
"Docks",
"Canal",
"Forum",
"Abbey",
"Castle",
"Garden",
"Mills",
"Heath",
"Depot"
],
"colorBy": "series",
"format": "integer",
"title": "Forum and Abbey stations exchanged 3,300 trips a day",
"subtitle": "Daily trips between pairs of ten stations on one tram line, both directions; each arc takes the colour of the station nearer the start of the line",
"note": "Illustrative figures for an invented line."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Stress: 18 entities with long names
import { ArcDiagram } from "insightcharts";
const data = [
{ a: "Customer support", b: "Marketing", messages: 33 },
{ a: "Customer support", b: "Data platform", messages: 22 },
{ a: "Customer support", b: "Finance", messages: 14 },
{ a: "Customer support", b: "Research", messages: 8 },
{ a: "Billing", b: "Product design", messages: 222 },
{ a: "Billing", b: "Security", messages: 189 },
{ a: "Billing", b: "People team", messages: 158 },
{ a: "Billing", b: "Partnerships", messages: 130 },
{ a: "Sales, north region", b: "Web engineering", messages: 602 },
{ a: "Sales, north region", b: "Quality assurance", messages: 544 },
{ a: "Sales, north region", b: "Facilities", messages: 489 },
{ a: "Sales, south region", b: "Mobile engineering", messages: 47 },
{ a: "Sales, south region", b: "Legal and compliance", messages: 33 },
{ a: "Sales, south region", b: "Executive office", messages: 22 },
{ a: "Marketing", b: "Data platform", messages: 259 },
{ a: "Marketing", b: "Finance", messages: 222 },
{ a: "Marketing", b: "Research", messages: 189 },
{ a: "Product design", b: "Security", messages: 663 },
{ a: "Product design", b: "People team", messages: 602 },
{ a: "Product design", b: "Partnerships", messages: 544 },
{ a: "Web engineering", b: "Quality assurance", messages: 64 },
{ a: "Web engineering", b: "Facilities", messages: 47 },
{ a: "Mobile engineering", b: "Legal and compliance", messages: 299 },
{ a: "Mobile engineering", b: "Executive office", messages: 259 },
{ a: "Data platform", b: "Finance", messages: 4 },
{ a: "Data platform", b: "Research", messages: 663 },
{ a: "Security", b: "People team", messages: 83 },
{ a: "Security", b: "Partnerships", messages: 64 },
{ a: "Quality assurance", b: "Facilities", messages: 342 },
{ a: "Legal and compliance", b: "Executive office", messages: 5 },
{ a: "Finance", b: "Research", messages: 105 },
{ a: "People team", b: "Partnerships", messages: 388 },
];
export function Example() {
return (
<ArcDiagram
data={data}
from="a"
to="b"
value="messages"
order={[
"Customer support",
"Billing",
"Sales, north region",
"Sales, south region",
"Marketing",
"Product design",
"Web engineering",
"Mobile engineering",
"Data platform",
"Security",
"Quality assurance",
"Legal and compliance",
"Finance",
"People team",
"Facilities",
"Executive office",
"Research",
"Partnerships",
]}
title="Messages between 18 teams in one week"
subtitle="Messages exchanged between pairs of teams at one company, one week; dots show each team's total"
note="Illustrative figures."
/>
);
}{
"type": "arc-diagram",
"data": [
{ "a": "Customer support", "b": "Marketing", "messages": 33 },
{ "a": "Customer support", "b": "Data platform", "messages": 22 },
{ "a": "Customer support", "b": "Finance", "messages": 14 },
{ "a": "Customer support", "b": "Research", "messages": 8 },
{ "a": "Billing", "b": "Product design", "messages": 222 },
{ "a": "Billing", "b": "Security", "messages": 189 },
{ "a": "Billing", "b": "People team", "messages": 158 },
{ "a": "Billing", "b": "Partnerships", "messages": 130 },
{ "a": "Sales, north region", "b": "Web engineering", "messages": 602 },
{ "a": "Sales, north region", "b": "Quality assurance", "messages": 544 },
{ "a": "Sales, north region", "b": "Facilities", "messages": 489 },
{ "a": "Sales, south region", "b": "Mobile engineering", "messages": 47 },
{ "a": "Sales, south region", "b": "Legal and compliance", "messages": 33 },
{ "a": "Sales, south region", "b": "Executive office", "messages": 22 },
{ "a": "Marketing", "b": "Data platform", "messages": 259 },
{ "a": "Marketing", "b": "Finance", "messages": 222 },
{ "a": "Marketing", "b": "Research", "messages": 189 },
{ "a": "Product design", "b": "Security", "messages": 663 },
{ "a": "Product design", "b": "People team", "messages": 602 },
{ "a": "Product design", "b": "Partnerships", "messages": 544 },
{ "a": "Web engineering", "b": "Quality assurance", "messages": 64 },
{ "a": "Web engineering", "b": "Facilities", "messages": 47 },
{ "a": "Mobile engineering", "b": "Legal and compliance", "messages": 299 },
{ "a": "Mobile engineering", "b": "Executive office", "messages": 259 },
{ "a": "Data platform", "b": "Finance", "messages": 4 },
{ "a": "Data platform", "b": "Research", "messages": 663 },
{ "a": "Security", "b": "People team", "messages": 83 },
{ "a": "Security", "b": "Partnerships", "messages": 64 },
{ "a": "Quality assurance", "b": "Facilities", "messages": 342 },
{ "a": "Legal and compliance", "b": "Executive office", "messages": 5 },
{ "a": "Finance", "b": "Research", "messages": 105 },
{ "a": "People team", "b": "Partnerships", "messages": 388 }
],
"from": "a",
"to": "b",
"value": "messages",
"order": [
"Customer support",
"Billing",
"Sales, north region",
"Sales, south region",
"Marketing",
"Product design",
"Web engineering",
"Mobile engineering",
"Data platform",
"Security",
"Quality assurance",
"Legal and compliance",
"Finance",
"People team",
"Facilities",
"Executive office",
"Research",
"Partnerships"
],
"title": "Messages between 18 teams in one week",
"subtitle": "Messages exchanged between pairs of teams at one company, one week; dots show each team's total",
"note": "Illustrative figures."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| fromrequired | string | Field naming one end of each connection. With select: true a click selects the entity under this field name, so inside <InsightData> other charts are filtered on from; pass select: "<field>" to link on the field that holds the entity in the shared rows. | |
| torequired | string | Field naming the other end. | |
| value | string | Value field: arc thickness. Omit to count rows. Rows joining the same pair are summed, in either direction. | |
| order | string[] | Entity names in the order they sit on the line. Entities not listed follow in data order. | |
| orientation | "auto" | "horizontal" | "vertical" | "auto" | Line along the bottom with arcs above, or down the left with arcs to the right. Auto turns vertical on narrow screens. |
| colorBy | "series" | "none" | series: each arc in the colour of its first entity; past the size of the theme's palette the extra colours are derived from the theme's own. none: every arc in the accent. Default: series while the entities fit the palette (at most 8), none beyond. | |
| arcLabels | boolean | true | Print values at the top of the arcs, largest first, wherever they do not collide. The values that do not fit are printed while their entity is hovered or selected, and every value is in the tooltip. |
| names | Record<string, string> | Display names for entity keys. |
Common chart props 29
Every chart built with defineChart accepts these on top of its own props.
| Prop | Type | Description |
|---|---|---|
| data | DataInput | Rows, CSV text, a URL, { url, path }, an async function, or an Arrow-style table. Omit inside <InsightData>. |
| transform | Transform | Transform[] | A pipeline of transforms applied before drawing. |
| title | string | The title. One plain statement of what the data shows, with the number and the period ("Monthly churn fell from 4.2% to 2.1% in 2024"), or a plain label ("Monthly churn, 2024"). One clause, no figures of speech. |
| subtitle | string | Measure, unit, population, period. |
| source | string | Data source, shown as "Source: ...". |
| note | string | Method notes or caveats. |
| height | number | string | CSS height of the plot. Default 360. |
| aspect | number | Width / height. Overrides height. |
| highlight | string | string[] | The entity (or entities) the title is about. Every other series is drawn in grey. |
| annotations | Annotation[] | Reference lines, shaded bands and callouts, in data coordinates. |
| format | FormatSpec | How to print values: "compact", "percent", "currency:USD", "{value} kg", Intl options, or a function. |
| legend | boolean | Show the legend. Default: shown when there are two or more series. |
| select | boolean | string | Make marks clickable to filter. Inside <InsightData> the selection filters every other chart. Pass a field name, or true to use the chart's category field. |
| onSelect | (values: string[], field: string) => void | Called with the selected values whenever the selection changes. |
| option | EChartsOption | ((option: EChartsOption, ctx: BuildContext) => EChartsOption) | Deep-merged over the generated ECharts option, or a function that receives and returns it. Use it for ECharts settings no prop covers. |
| theme | ThemeInput | A theme, or a partial set of tokens merged over the surrounding theme, for this chart only. |
| mode | ModeInput | "auto" (default) follows the page, "system" follows the OS, or force "light" / "dark". |
| renderer | RendererInput | "auto" (default) uses SVG for light charts and canvas for heavy ones. |
| animate | boolean | Set false to disable animation. Reduced-motion preferences are always respected. |
| lazy | boolean | Mount the chart only when it scrolls near the viewport. Default true. |
| group | string | Link tooltips with other charts sharing this group id. |
| exportable | boolean | ExportFormat[] | Show an export menu: true or a list of formats from png, jpeg, svg, pdf, csv, json. |
| table | boolean | Show a collapsible "View data" table under the chart. |
| alt | string | Accessible description: the chart type and measures, then the main pattern and the notable values. |
| id | string | Stable id for this chart's selection in the shared store. |
| onEvents | Record<string, ChartEventHandler> | ECharts event handlers, e.g. { click: (params, chart) => {} }. Prefix a name with zr: to bind to the canvas itself. |
| onReady | (chart: EChartsInstance) => void | Called with the ECharts instance once it is created. |
| className | string | Class name for the outer <figure>. |
| style | CSSProperties | Inline style for the outer <figure>. |