Rapid Rails UI Pro

—

Upgrade to unlock this component

Get Pro →

Chart Gallery

Every chart type and option of rui_chart, live, with the code that renders it. Each code block is generated from the same options as the chart above it, so what you copy is what you see.

The reference for each option is the Chart page . Figures here are illustrative; applications are named by file number, never by address.

Dashboard Patterns

Common dashboard cards built only from existing options. Each card is your own markup around one rui_chart.

KPI tiles

A number, a delta and a sparkline. The tile is your markup; the sparkline is type: :sparkline, line or variant: :column.

Reports this month

214 +12%
Sparkline data
IndexValue
1150
2162
3158
4171
5180
6176
7191
8214

Median days to close

9 -2 days
Sparkline data
IndexValue
114
213
312
412
511
610
711
89

Open applications

31 +3
Sparkline data
IndexValue
124
225
327
426
528
628
729
831

Net change, homes

+410 vs +120
Sparkline data
IndexValue
140
220
360
410
530
690
7120
8140
Show code
KPI tile
<div class="p-4 rounded-lg border">
  <p class="text-sm">Reports this month</p>
  <div class="flex items-baseline gap-2">
    <span class="text-3xl font-semibold">214</span>
    <%= rui_badge("+12%", color: :success, size: :sm) %>
  </div>
  <%= rui_chart(type: :sparkline, values: [150, 162, 158, 171, 180, 176, 191, 214]) %>
</div>

Leaderboard

hbar with a highlighted row, values on the bars, and max_rows: folding the tail into Other.

Requests by type
Last 30 days
  • Pothole
  • Graffiti
  • Bulky items
  • Streetlight
  • Noise
  • Litter
  • Other 3
0 500 1k 1.5k
Requests by type
Category Value
Pothole 1.2k
Graffiti 860
Bulky items 640
Streetlight 410
Noise 380
Litter 300
Other 3 550
Show code
Leaderboard
<%= rui_chart(
  type: :hbar,
  title: "Requests by type",
  subtitle: "Last 30 days",
  value_format: :compact,
  show_values: true,
  max_rows: 6,
  data: [
    { label: "Pothole", value: 1210 },
    { label: "Graffiti", value: 860, highlight: true },
    { label: "Bulky items", value: 640 },
    { label: "Streetlight", value: 410 },
    { label: "Noise", value: 380 },
    { label: "Litter", value: 300 },
    { label: "Signals", value: 220 },
    { label: "Sidewalk", value: 180 },
    { label: "Trees", value: 150 }
  ]) %>

Share over time

There is no 100% column option: compute each share in Ruby, then stack with y_max: 100 and value_format: :percent.

Channel share by quarter
0% 20% 40% 60% 80% 100%
100% 100% 100% 100%
Q1 Q2 Q3 Q4
Channel share by quarter
Category OnlinePhoneIn person
Q1 52%35%13%
Q2 58%31%11%
Q3 61%29%10%
Q4 66%25%9%
Show code
Share over time
<%= rui_chart(
  type: :bar,
  layout: :stacked,
  title: "Channel share by quarter",
  categories: ["Q1", "Q2", "Q3", "Q4"],
  y_max: 100,
  value_format: :percent,
  series: [
    { name: "Online", values: [52, 58, 61, 66] },
    { name: "Phone", values: [35, 31, 29, 25] },
    { name: "In person", values: [13, 11, 10, 9] }
  ]) %>

Target vs actual

A reference line for the target and an annotation for the month that changed.

Repairs per month
0 20 40 60
Target
Jan Feb Mar Apr May Jun
  • Apr: Extra crew
Repairs per month
Category repairs
Jan 38 repairs
Feb 42 repairs
Mar 47 repairs
Apr 51 repairs
May 44 repairs
Jun 53 repairs
Show code
Target vs actual
<%= rui_chart(
  type: :bar,
  title: "Repairs per month",
  unit: "repairs",
  references: [{ value: 45, label: "Target" }],
  annotations: [{ at: "Apr", text: "Extra crew" }],
  data: [
    { label: "Jan", value: 38 },
    { label: "Feb", value: 42 },
    { label: "Mar", value: 47 },
    { label: "Apr", value: 51 },
    { label: "May", value: 44 },
    { label: "Jun", value: 53 }
  ]) %>

Forecast

projection: continues the last point; band: shows the range.

Requests per year
Range if growth holds
  • Requests
Requests per year
Category Requests
2019 410
2020 380
2021 455
2022 490
2023 530
2024 575
2025 610
2027 0
Show code
Forecast
<%= rui_chart(
  type: :line,
  title: "Requests per year",
  value_format: :number,
  series: [
    { name: "Requests", points: [
        ["2019", 410],
        ["2020", 380],
        ["2021", 455],
        ["2022", 490],
        ["2023", 530],
        ["2024", 575],
        ["2025", 610]
      ] }
  ],
  projection: { from: "2025", to: "2027", value: 700 },
  band: { at: "2027", low: 660, high: 745, label: "Range if growth holds" }) %>

Compare series

crosshair: shows every series at the hovered category; end_labels: names each line at its end.

Requests by channel
Online Phone
Requests by channel
Category OnlinePhone
Jan 12090
Feb 13586
Mar 15084
Apr 16280
May 17177
Jun 18872
Show code
Compare series
<%= rui_chart(
  type: :area,
  title: "Requests by channel",
  crosshair: true,
  end_labels: true,
  value_format: :number,
  series: [
    { name: "Online", points: [
        ["Jan", 120],
        ["Feb", 135],
        ["Mar", 150],
        ["Apr", 162],
        ["May", 171],
        ["Jun", 188]
      ] },
    { name: "Phone", points: [
        ["Jan", 90],
        ["Feb", 86],
        ["Mar", 84],
        ["Apr", 80],
        ["May", 77],
        ["Jun", 72]
      ] }
  ]) %>

Activity

A weekday by hour heatmap.

Requests by weekday and hour
6a
10a
2p
6p
10p
Mon
Tue
Wed
Thu
Fri
Sat
Sun
0 11 requests
Requests by weekday and hour
Row6a8a10a12p2p4p6p8p10p
Mon2 requests10 requests8 requests5 requests8 requests10 requests11 requests7 requests4 requests
Tue2 requests10 requests8 requests5 requests8 requests10 requests11 requests7 requests4 requests
Wed2 requests10 requests8 requests5 requests8 requests10 requests11 requests7 requests4 requests
Thu2 requests10 requests8 requests5 requests8 requests10 requests11 requests7 requests4 requests
Fri2 requests10 requests8 requests5 requests8 requests10 requests11 requests7 requests4 requests
Sat0 requests3 requests7 requests7 requests9 requests9 requests5 requests4 requests3 requests
Sun0 requests3 requests7 requests7 requests9 requests9 requests5 requests4 requests3 requests
Show code
Activity
<%= rui_chart(
  type: :heatmap,
  title: "Requests by weekday and hour",
  unit: "requests",
  rows: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
  columns: ["6a", "8a", "10a", "12p", "2p", "4p", "6p", "8p", "10p"],
  values: [
    [2, 10, 8, 5, 8, 10, 11, 7, 4],
    [2, 10, 8, 5, 8, 10, 11, 7, 4],
    [2, 10, 8, 5, 8, 10, 11, 7, 4],
    [2, 10, 8, 5, 8, 10, 11, 7, 4],
    [2, 10, 8, 5, 8, 10, 11, 7, 4],
    [0, 3, 7, 7, 9, 9, 5, 4, 3],
    [0, 3, 7, 7, 9, 9, 5, 4, 3]
  ]) %>

Status history

A timeline strip pinned with as_of: so a saved report reads the same later.

Permits in review
  • Permit 22-14
  • Permit 23-07
  • Permit 24-31
  • Permit 25-02
2022 2023 2024 2025 2026
  • review
  • issued
Permits in review
ItemStatusFromToDays
Permit 22-14reviewMar 1, 2022Jan 15, 2023320
Permit 22-14issuedJan 15, 2023Feb 1, 202317
Permit 23-07reviewMay 10, 2023ongoing1,253
Permit 24-31reviewFeb 20, 2024Nov 5, 2024259
Permit 24-31issuedNov 5, 2024Nov 20, 202415
Permit 25-02reviewJun 1, 2025ongoing500
Show code
Status history
<%= rui_chart(
  type: :timeline_strip,
  title: "Permits in review",
  as_of: "2026-10-14",
  domain: { from: "2022-01-01", to: "today" },
  groups: { "review" => 0, "issued" => 1 },
  sort: :start,
  rows: [
    { label: "Permit 22-14", segments: [
        { from: "2022-03-01", to: "2023-01-15", group: "review" },
        { from: "2023-01-15", to: "2023-02-01", group: "issued" }
      ] },
    { label: "Permit 23-07", segments: [{ from: "2023-05-10", group: "review" }] },
    { label: "Permit 24-31", segments: [
        { from: "2024-02-20", to: "2024-11-05", group: "review" },
        { from: "2024-11-05", to: "2024-11-20", group: "issued" }
      ] },
    { label: "Permit 25-02", segments: [{ from: "2025-06-01", group: "review" }] }
  ]) %>

Distribution

A histogram of days to close.

Days to close a request
0 2 4 6 8
0 to 2 3 to 6 7 to 13 14 to 29 30 to 60
Days to close a request
Category requests
0 to 2 5 requests
3 to 6 7 requests
7 to 13 6 requests
14 to 29 5 requests
30 to 60 3 requests
Show code
Distribution
<%= rui_chart(
  type: :histogram,
  title: "Days to close a request",
  unit: "requests",
  bin_edges: [0, 3, 7, 14, 30, 60],
  values: [1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 6, 6, 8, 9, 9, 10, 12, 13, 15, 18, 21, 25, 29, 33, 41, 52]) %>

Story

Play through the years with your own captions and a count-up readout.

Homes approved per year
2025 1,970 homes approved

2025: the busiest year

0 200 400 600 800
2021 2022 2023 2024 2025
Homes approved per year
Category Approved
2021 210
2022 340
2023 290
2024 520
2025 610
Show code
Story
<%= rui_chart(
  type: :bar,
  title: "Homes approved per year",
  id: "pattern-story-chart",
  unit: "homes",
  value_format: :number,
  categories: ["2021", "2022", "2023", "2024", "2025"],
  series: [{ name: "Approved", values: [210, 340, 290, 520, 610] }],
  timeline: { label: "Play 2021 to 2025", captions: ["2021: a slow start", nil, "2023: one large file withdrawn", nil, "2025: the busiest year"], readout: { mode: :cumulative, label: "homes approved" } }) %>

Phones

responsive: :hbar turns ten columns into rows below 480 px.

Requests by ward
  • Ward 1
  • Ward 2
  • Ward 3
  • Ward 4
  • Ward 5
  • Ward 6
  • Ward 7
  • Ward 8
  • Ward 9
  • Ward 10
0 20 40 60 80 100
Requests by ward
Category Value
Ward 1 57
Ward 2 74
Ward 3 91
Ward 4 48
Ward 5 65
Ward 6 82
Ward 7 99
Ward 8 56
Ward 9 73
Ward 10 90
Show code
Phones
<%= rui_chart(
  type: :bar,
  responsive: :hbar,
  title: "Requests by ward",
  value_format: :number,
  data: [
    { label: "Ward 1", value: 57 },
    { label: "Ward 2", value: 74 },
    { label: "Ward 3", value: 91 },
    { label: "Ward 4", value: 48 },
    { label: "Ward 5", value: 65 },
    { label: "Ward 6", value: 82 },
    { label: "Ward 7", value: 99 },
    { label: "Ward 8", value: 56 },
    { label: "Ward 9", value: 73 },
    { label: "Ward 10", value: 90 }
  ]) %>

Brand theming

Series colours come from --rui-chart-1 to --rui-chart-8; text, grid and tooltip from the chrome variables. Set them on :root or on one card. The dark card also sets color-scheme: dark, which flips every chrome fallback you leave unset.

Brand colours
Light card
0 20 40 60 80
Q1 Q2 Q3 Q4
Brand colours
Category OnlinePhone
Q1 5235
Q2 5831
Q3 6129
Q4 6625
Brand colours
Dark card
0 20 40 60 80
Q1 Q2 Q3 Q4
Brand colours
Category OnlinePhone
Q1 5235
Q2 5831
Q3 6129
Q4 6625
Show code
app/assets/tailwind/application.css
:root {
  --rui-chart-1: #0f766e;
  --rui-chart-2: #f59e0b;
  --rui-chart-bar-radius: 2px;
}

.card-dark {
  color-scheme: dark;
  --rui-chart-1: #2dd4bf;
  --rui-chart-2: #fbbf24;
  --rui-chart-text: #f4f4f5;
  --rui-chart-muted: #a1a1aa;
  --rui-chart-grid: #3f3f46;
}