Compares a value across categories with bars from a zero baseline.
Poster
The sheet an infographic is composed on. It paints a full-bleed ground from a poster theme, forces one theme and colour mode on everything inside, and lays out a masthead, a grid of panels and a footer. It can save the whole sheet as one PNG.
- Use when
- Use it as the outer frame of any infographic: one dominant graphic with a few supporting panels, on a painted ground. Give it a poster theme (
noir,magenta,dusk,paper,ocean,forest,blueprint,sunrise) and put a chart or figure spec in each panel. Put the one fact intitleand keep panel headings short. It reflows to one column under 600px. - Avoid when
- Long reading text or more than about six panels (use report page or visual essay). A single fact at a fixed social size (use social card). Charts that need axes, gridlines and dense labels to be read, because a poster ground suits large flat marks with their values printed on them.
Preview
The preview scrolls inside its frame. A poster has the same colours in the site's light and dark modes. The toolbar has the poster themes and a PNG download.
import { Poster, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<Poster
theme="noir"
kicker="Transport"
title="Riverton's bike-share scheme carried 4.2 million trips in 2025"
subtitle="Trips, bikes and reasons for riding in the city's public bike-share scheme, January to December 2025."
panels={[
{
span: 4,
valign: "center",
content: {
type: "big-number",
size: "hero",
value: "4.2 million",
label: "Trips in 2025",
context: "3.4 million in 2024",
},
},
{
span: 8,
title: "Old Town had 1,150 of the scheme's 3,000 bikes",
content: {
type: "pictogram-chart",
data: [
{ district: "Old Town", bikes: 1150 },
{ district: "Harbour", bikes: 820 },
{ district: "University", bikes: 640 },
{ district: "Northside", bikes: 390 },
],
name: "district",
value: "bikes",
unit: 50,
unitLabel: "50 bikes",
icon: "bike",
highlight: "Old Town",
},
source: "Riverton Transport Office",
},
{
span: 7,
title: "670,000 trips were made on Wednesdays in 2025",
subtitle: "Trips by day of the week, thousands, 2025",
content: {
type: "bar-chart",
data: [
{ day: "Mon", trips: 610 },
{ day: "Tue", trips: 655 },
{ day: "Wed", trips: 670 },
{ day: "Thu", trips: 662 },
{ day: "Fri", trips: 640 },
{ day: "Sat", trips: 520 },
{ day: "Sun", trips: 443 },
],
x: "day",
y: "trips",
orientation: "vertical",
sort: "none",
highlight: "Wed",
format: "{value}k",
height: 260,
},
},
{
span: 5,
title: "46% of trips were commutes",
subtitle: "Share of trips by stated purpose, rider survey, 2025",
content: {
type: "donut-chart",
data: [
{ purpose: "Commuting", share: 46 },
{ purpose: "Leisure", share: 27 },
{ purpose: "Errands", share: 18 },
{ purpose: "Other", share: 9 },
],
name: "purpose",
value: "share",
format: "{value}%",
centerValue: "46%",
centerLabel: "Commuting",
height: 260,
},
},
]}
sources={["Riverton Transport Office", "Riverton rider survey 2025"]}
note="Riverton is an invented city. All figures are illustrative."
credit="InsightCharts"
/>
);
}{
"type": "poster",
"theme": "noir",
"kicker": "Transport",
"title": "Riverton's bike-share scheme carried 4.2 million trips in 2025",
"subtitle": "Trips, bikes and reasons for riding in the city's public bike-share scheme, January to December 2025.",
"panels": [
{
"span": 4,
"valign": "center",
"content": {
"type": "big-number",
"size": "hero",
"value": "4.2 million",
"label": "Trips in 2025",
"context": "3.4 million in 2024"
}
},
{
"span": 8,
"title": "Old Town had 1,150 of the scheme's 3,000 bikes",
"content": {
"type": "pictogram-chart",
"data": [
{ "district": "Old Town", "bikes": 1150 },
{ "district": "Harbour", "bikes": 820 },
{ "district": "University", "bikes": 640 },
{ "district": "Northside", "bikes": 390 }
],
"name": "district",
"value": "bikes",
"unit": 50,
"unitLabel": "50 bikes",
"icon": "bike",
"highlight": "Old Town"
},
"source": "Riverton Transport Office"
},
{
"span": 7,
"title": "670,000 trips were made on Wednesdays in 2025",
"subtitle": "Trips by day of the week, thousands, 2025",
"content": {
"type": "bar-chart",
"data": [
{ "day": "Mon", "trips": 610 },
{ "day": "Tue", "trips": 655 },
{ "day": "Wed", "trips": 670 },
{ "day": "Thu", "trips": 662 },
{ "day": "Fri", "trips": 640 },
{ "day": "Sat", "trips": 520 },
{ "day": "Sun", "trips": 443 }
],
"x": "day",
"y": "trips",
"orientation": "vertical",
"sort": "none",
"highlight": "Wed",
"format": "{value}k",
"height": 260
}
},
{
"span": 5,
"title": "46% of trips were commutes",
"subtitle": "Share of trips by stated purpose, rider survey, 2025",
"content": {
"type": "donut-chart",
"data": [
{ "purpose": "Commuting", "share": 46 },
{ "purpose": "Leisure", "share": 27 },
{ "purpose": "Errands", "share": 18 },
{ "purpose": "Other", "share": 9 }
],
"name": "purpose",
"value": "share",
"format": "{value}%",
"centerValue": "46%",
"centerLabel": "Commuting",
"height": 260
}
}
],
"sources": ["Riverton Transport Office", "Riverton rider survey 2025"],
"note": "Riverton is an invented city. All figures are illustrative.",
"credit": "InsightCharts"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Built from
The pieces poster renders or expects in its slots.
Atoms
8A featured statistic with its label, change and the context that gives it meaning.
Shows how 2 to 5 parts make up one whole, with each slice labelled by name and share.
A "1 in 4"-style statement: a row or grid of simple glyphs with some filled, and the sentence they illustrate.
Compares quantities with rows of repeated icons, each icon standing for a fixed number of units.
Splits one whole into proportional segments along a single horizontal bar.
Shows parts of a whole as nested rectangles sized by value, optionally grouped into categories.
Shows shares of a whole as counted cells in a grid, such as 37 in every 100.
Used in
10 infographics use poster.
Infographics
10A landscape spread of up to eleven small illustrations of one subject, each a different graphic form, in three bands of unequal panels: a statement in large type, spokes round an icon and a network of circles; a smooth trend with its peak labelled, nested squares and a slope chart; a pill ranking, a treemap, a tile map and sized circles; and a closing row of icon statements.
A poster that counts at scale. Blocks of thousands of unit icons fill most of the sheet, a second band counts reference groups with the same icon at the same size in a muted colour, and a bottom row holds three small graphics: squares sized by area with a ratio bracket, thick bars with the numbers inside, and shares as nested outlines or filled silhouettes.
A portrait poster of where something comes from and where it goes, in three stacked bands painted in one colour: the producers as large labelled tiles, the leading producer's output fanning out as ribbons to an aligned column of destinations, and the destinations as bubbles on a scale of how much each depends on that producer.
One wide landscape graphic that compares 4 to 12 entities on five to seven measures: two measures as S-curves between parallel scales on the left, joined to a table on the right in which every other number is drawn as dots, squares, icons, a ring, a circle or a bar. Both halves share one row order and one colour per entity.
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).
A poster that describes one subject from four sides: the subject as one large silhouette filled to a share (shape-fill), two to four large figures, its place among other things on one scale (scale-ladder), and a table that sets it beside its peers with every number drawn as a small form (glyph-table).
A poster with one subject in the middle of the sheet: a wheel of spokes round an icon (radial-spokes), two to four large figures standing either side of it, and a bottom row with the same subject's surroundings on concentric rings (orbit-chart) and a ranking as centred pills (pill-rank).
A poster of one subject by the numbers, in four rows: two figures in large type or as filled silhouettes, two treemaps side by side, a full-width ranking of centred pills with one row highlighted, and packed circles with icons beside a column of sized circles and a closing figure.
A tall poster that is one illustrated scale: a full-height scale-ladder with icons, leader-labelled items and named zones, and a narrow side column of large figures and one small graphic, each set level with the part of the scale it describes.
A poster of change over a run of periods: a full-width ribbon of bands whose thickness is each entity's value and whose order is its rank (ribbon-chart), a strip of two to four dated figures under it, and a bottom row with one measure per period as thick bars (block-bar) and one share at several dates as nested outlines (nested-shares).
Installation
$ pnpm dlx shadcn@latest add @insightcharts/posterThe 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 { Poster } from "insightcharts";Examples
Magenta, one dominant graphic, with download
import { Poster, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<Poster
theme="magenta"
kicker="Food"
title="Households in Norland threw away 61 kg of food per person in 2025"
subtitle="Food thrown away at home, kilograms per person, by type of food."
download
panels={[
{
content: {
type: "treemap",
data: [
{ food: "Vegetables", kg: 14.2 },
{ food: "Bread and bakery", kg: 11.5 },
{ food: "Fruit", kg: 9.8 },
{ food: "Dairy and eggs", kg: 8.1 },
{ food: "Cooked leftovers", kg: 7.6 },
{ food: "Meat and fish", kg: 5.3 },
{ food: "Drinks", kg: 2.7 },
{ food: "Other", kg: 1.8 },
],
name: "food",
value: "kg",
format: "{value} kg",
height: 420,
},
source: "Norland Waste Agency household survey",
},
{
span: 6,
title: "64% of the food thrown away could have been eaten",
subtitle: "Each square is 1% of the weight thrown away",
content: {
type: "waffle-chart",
data: [
{ group: "Could have been eaten", share: 64 },
],
name: "group",
value: "share",
total: 100,
restLabel: "Peel, bones and shells",
rows: 5,
height: 190,
},
},
{
span: 6,
valign: "center",
content: {
type: "big-number",
size: "hero",
value: "€412",
label: "Cost of the food one household threw away in 2025",
context: "€388 in 2024",
},
},
]}
sources="Norland Waste Agency"
note="Norland is an invented country. All figures are illustrative."
credit="InsightCharts"
/>
);
}{
"type": "poster",
"theme": "magenta",
"kicker": "Food",
"title": "Households in Norland threw away 61 kg of food per person in 2025",
"subtitle": "Food thrown away at home, kilograms per person, by type of food.",
"download": true,
"panels": [
{
"content": {
"type": "treemap",
"data": [
{ "food": "Vegetables", "kg": 14.2 },
{ "food": "Bread and bakery", "kg": 11.5 },
{ "food": "Fruit", "kg": 9.8 },
{ "food": "Dairy and eggs", "kg": 8.1 },
{ "food": "Cooked leftovers", "kg": 7.6 },
{ "food": "Meat and fish", "kg": 5.3 },
{ "food": "Drinks", "kg": 2.7 },
{ "food": "Other", "kg": 1.8 }
],
"name": "food",
"value": "kg",
"format": "{value} kg",
"height": 420
},
"source": "Norland Waste Agency household survey"
},
{
"span": 6,
"title": "64% of the food thrown away could have been eaten",
"subtitle": "Each square is 1% of the weight thrown away",
"content": {
"type": "waffle-chart",
"data": [
{ "group": "Could have been eaten", "share": 64 }
],
"name": "group",
"value": "share",
"total": 100,
"restLabel": "Peel, bones and shells",
"rows": 5,
"height": 190
}
},
{
"span": 6,
"valign": "center",
"content": {
"type": "big-number",
"size": "hero",
"value": "€412",
"label": "Cost of the food one household threw away in 2025",
"context": "€388 in 2024"
}
}
],
"sources": "Norland Waste Agency",
"note": "Norland is an invented country. All figures are illustrative.",
"credit": "InsightCharts"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Dusk, gradient ground, fixed 4:5 shape
import { Poster, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<Poster
theme="dusk"
aspect="4:5"
maxWidth={820}
columns={2}
kicker="Energy"
title="Solar supplied 31% of Port Alba's electricity in 2025"
panels={[
{
span: 2,
title: "Solar generated 3.1 TWh of the island's 10 TWh",
content: {
type: "pictogram-chart",
data: [
{ source: "Solar", twh: 3.1 },
{ source: "Wind", twh: 2.6 },
{ source: "Gas", twh: 2.9 },
{ source: "Hydro", twh: 1.4 },
],
name: "source",
value: "twh",
unit: 0.2,
unitLabel: "0.2 TWh",
icons: { Solar: "solar-panel", Wind: "wind-turbine", Gas: "flame", Hydro: "drop" },
icon: "bolt",
highlight: "Solar",
format: "{value} TWh",
},
},
{
span: 2,
title: "Solar's share was 51% in July and 8% in December",
subtitle: "Solar share of electricity generated, by month, 2025",
content: {
type: "bar-chart",
data: [
{ month: "Jan", share: 9 },
{ month: "Feb", share: 14 },
{ month: "Mar", share: 24 },
{ month: "Apr", share: 35 },
{ month: "May", share: 44 },
{ month: "Jun", share: 49 },
{ month: "Jul", share: 51 },
{ month: "Aug", share: 46 },
{ month: "Sep", share: 36 },
{ month: "Oct", share: 24 },
{ month: "Nov", share: 13 },
{ month: "Dec", share: 8 },
],
x: "month",
y: "share",
orientation: "vertical",
sort: "none",
highlight: "Jul",
format: "{value}%",
height: 240,
},
},
{
span: 1,
content: {
type: "big-number",
size: "hero",
value: "31%",
label: "Solar share of electricity in 2025",
},
},
{
span: 1,
content: {
type: "big-number",
size: "hero",
value: "12%",
label: "Solar share of electricity in 2020",
},
},
]}
sources="Port Alba Grid Operator"
note="Port Alba is an invented island. All figures are illustrative."
credit="InsightCharts"
/>
);
}{
"type": "poster",
"theme": "dusk",
"aspect": "4:5",
"maxWidth": 820,
"columns": 2,
"kicker": "Energy",
"title": "Solar supplied 31% of Port Alba's electricity in 2025",
"panels": [
{
"span": 2,
"title": "Solar generated 3.1 TWh of the island's 10 TWh",
"content": {
"type": "pictogram-chart",
"data": [
{ "source": "Solar", "twh": 3.1 },
{ "source": "Wind", "twh": 2.6 },
{ "source": "Gas", "twh": 2.9 },
{ "source": "Hydro", "twh": 1.4 }
],
"name": "source",
"value": "twh",
"unit": 0.2,
"unitLabel": "0.2 TWh",
"icons": {
"Solar": "solar-panel",
"Wind": "wind-turbine",
"Gas": "flame",
"Hydro": "drop"
},
"icon": "bolt",
"highlight": "Solar",
"format": "{value} TWh"
}
},
{
"span": 2,
"title": "Solar's share was 51% in July and 8% in December",
"subtitle": "Solar share of electricity generated, by month, 2025",
"content": {
"type": "bar-chart",
"data": [
{ "month": "Jan", "share": 9 },
{ "month": "Feb", "share": 14 },
{ "month": "Mar", "share": 24 },
{ "month": "Apr", "share": 35 },
{ "month": "May", "share": 44 },
{ "month": "Jun", "share": 49 },
{ "month": "Jul", "share": 51 },
{ "month": "Aug", "share": 46 },
{ "month": "Sep", "share": 36 },
{ "month": "Oct", "share": 24 },
{ "month": "Nov", "share": 13 },
{ "month": "Dec", "share": 8 }
],
"x": "month",
"y": "share",
"orientation": "vertical",
"sort": "none",
"highlight": "Jul",
"format": "{value}%",
"height": 240
}
},
{
"span": 1,
"content": {
"type": "big-number",
"size": "hero",
"value": "31%",
"label": "Solar share of electricity in 2025"
}
},
{
"span": 1,
"content": {
"type": "big-number",
"size": "hero",
"value": "12%",
"label": "Solar share of electricity in 2020"
}
}
],
"sources": "Port Alba Grid Operator",
"note": "Port Alba is an invented island. All figures are illustrative.",
"credit": "InsightCharts"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Paper, three columns, solid rules, centred masthead
import { Poster, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<Poster
theme="paper"
columns={3}
rules="solid"
titleAlign="center"
kicker="Libraries"
title="Hollin Valley's libraries lent 318,000 items in 2025"
subtitle="Loans from the valley's six public libraries, January to December 2025."
panels={[
{
span: 2,
title: "Printed books were 171,000 of the 318,000 loans",
subtitle: "Loans by type of item, thousands",
content: {
type: "segmented-bar",
data: [
{ kind: "Printed books", items: 171 },
{ kind: "E-books", items: 74 },
{ kind: "Audiobooks", items: 48 },
{ kind: "Films and music", items: 25 },
],
name: "kind",
value: "items",
format: "{value}k",
},
source: "Hollin Valley Library Service",
},
{
span: 1,
valign: "center",
content: {
type: "big-number",
size: "hero",
value: "318,000",
label: "Items lent in 2025",
context: "301,000 in 2024",
},
},
{
span: 3,
content: {
type: "icon-stat",
icon: "book",
count: 3,
total: 10,
value: "3 in 10",
text: "residents borrowed at least one item in 2025.",
},
},
]}
sources="Hollin Valley Library Service annual return"
note="Hollin Valley is an invented place. All figures are illustrative."
credit="InsightCharts"
/>
);
}{
"type": "poster",
"theme": "paper",
"columns": 3,
"rules": "solid",
"titleAlign": "center",
"kicker": "Libraries",
"title": "Hollin Valley's libraries lent 318,000 items in 2025",
"subtitle": "Loans from the valley's six public libraries, January to December 2025.",
"panels": [
{
"span": 2,
"title": "Printed books were 171,000 of the 318,000 loans",
"subtitle": "Loans by type of item, thousands",
"content": {
"type": "segmented-bar",
"data": [
{ "kind": "Printed books", "items": 171 },
{ "kind": "E-books", "items": 74 },
{ "kind": "Audiobooks", "items": 48 },
{ "kind": "Films and music", "items": 25 }
],
"name": "kind",
"value": "items",
"format": "{value}k"
},
"source": "Hollin Valley Library Service"
},
{
"span": 1,
"valign": "center",
"content": {
"type": "big-number",
"size": "hero",
"value": "318,000",
"label": "Items lent in 2025",
"context": "301,000 in 2024"
}
},
{
"span": 3,
"content": {
"type": "icon-stat",
"icon": "book",
"count": 3,
"total": 10,
"value": "3 in 10",
"text": "residents borrowed at least one item in 2025."
}
}
],
"sources": "Hollin Valley Library Service annual return",
"note": "Hollin Valley is an invented place. All figures are illustrative.",
"credit": "InsightCharts"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | The poster's one fact, as a plain sentence with its number. Set large and light in the theme's display face. | |
| kicker | string | Small label above the title: the topic or series. | |
| subtitle | string | One or two lines under the title: the measure, population and period. | |
| panelsrequired | PosterPanel[] | The blocks, in reading order. Each is { content, title?, subtitle?, span?, rowSpan?, source?, note?, align?, valign?, bleed?, id? }. content is a component spec or rendered content. span is in units of columns (default: full width). bleed runs the content to the poster's left and right edges. | |
| columns | number | 12 | Columns of the layout grid: a 12-unit grid, or a simple count such as 2 or 3. Under 600px every panel is full width; from 600 to 760px the grid has two columns. |
| gap | number | Space between panels in px. Default 40 (28 under 600px). | |
| padding | number | Space between the poster's edge and its content in px. Default 56 (36 under 900px, 20 under 600px). | |
| maxWidth | number | string | 1200 | Widest the poster is drawn. It is centred in wider containers. |
| aspect | number | string | Fixed shape as width / height: 0.75, "3:4", "16:9". The poster still grows when its content needs more height, and the ratio is dropped under 600px. | |
| theme | PosterThemeName | string | ThemeInput | "noir" | A poster theme name, any other theme name, or a theme object. It is passed on to everything inside. |
| mode | "light" | "dark" | "auto" | "system" | "light" | Colour mode of the poster and everything in it. A poster does not follow the page unless you set auto. Only paper has a different dark variant. |
| background | string | CSS background of the sheet, a colour or a gradient. Default: the poster theme's ground. Keep it close to the theme's surface, which charts use for the gaps between marks. | |
| rules | "none" | "solid" | "dotted" | "dotted" | Lines under the masthead, between rows of panels and above the footer. |
| sources | string | string[] | Where the data comes from, shown in the footer. | |
| note | string | Caveat shown in the footer, e.g. "Illustrative data". | |
| credit | string | Who made the poster, shown at the right of the footer. | |
| titleAlign | "left" | "center" | "left" | Alignment of the masthead. |
| level | 1 | 2 | 3 | 4 | 5 | 2 | Heading level of the title. Panel headings are one level down. |
| download | boolean | false | Show a "Download PNG" control under the poster. The image is the whole sheet, drawn from the DOM onto a canvas, without the control. |
| exportWidth | number | 2400 | Width of the downloaded PNG in pixels. |
| filename | string | File name without extension. Defaults to a slug of the title. | |
| downloadLabel | string | "Download PNG" | Label of the download control. |
| onExport | (blob: Blob) => void | Called with the PNG after a download. |