Skip to content

Render kinds and options

The trailing RENDER <kind> [WITH (…)] clause decides how a result is displayed. A query with no RENDER clause renders as a table.

KindNameDisplays
listListBulleted text list.
tableTablePlain data table.
leaderboardLeaderboardRanked leaderboard text, optionally @mentioning top rows.

leaderboard is the only kind that @mentions people, and it takes its own option: mentions = <n> pings the top n ranked rows, mentions = all pings every eligible row, and mentions = 0 pings nobody. Omitted, it pings the top three. Rows grouped by anything other than a player never mention anyone, even when a champion name happens to match an alias.

KindNameDisplays
bar_chartBar chartCategorical bar chart.
line_chartLine chartCategorical or temporal line chart.
stacked_barStacked barStacked multi-output bar chart.
area_chartArea chartFilled categorical or temporal trend.
donut_chartDonut chartPart-to-whole chart for bounded rows.
scatter_chartScatter chartCompare two outputs, optionally sized by a third.
heatmapHeatmapTwo-dimensional matrix colored by an output.
radar_chartRadar chartCompare three to eight normalized outputs.
kpi_cardKPI cardOne aggregate row displayed as metric cards.
bump_chartBump chartRank-position movement over time.
calendar_heatmapCalendar heatmapDaily activity or performance on a calendar grid.
histogramHistogramDistribution over a numeric bucket grouping, such as FLOOR(game_duration_seconds / 300) * 300.
box_plotBox plotFive-number summary per group: min, q1, median, q3 and max, named in that order as y.

Charts are rendered to an image and posted as an attachment, so the destination channel needs the Attach Files permission.

OptionSyntaxEffect
xx = <output>Horizontal channel; defaults to the grouping.
yy = <output> | (a, b, …)The numeric series, up to eight.
seriesseries = <grouping>Which grouping splits into series.
sizesize = <output>Sizes scatter points.
valuevalue = <output>The cell value of a heatmap.
titletitle = '…'Overrides the report's title on the image.
subtitlesubtitle = '…'A line under the title.
x_axisx_axis = '…'Label for the horizontal axis.
y_axisy_axis = '…'Label for the vertical axis.
themetheme = <name>Chart theme: one of lol_dark, lol_light, minimal_dark, minimal_light.
palettepalette = <name>Series colours: one of ranked, categorical, team, gold, colorblind.
colorscolors = (#rrggbb, …)One to eight custom colours, contrast-corrected for the theme.
orientationorientation = <name>Bar direction: one of horizontal, vertical.
labelslabels = <name>Value labels on marks: one of auto, show, hide, value, percent.
legendlegend = <name>Legend placement: one of auto, none, top, right, bottom.
sortsort = <name>Visual row order without changing the query: one of query, asc, desc.
smoothsmooth = trueCurves line and area segments.
rollingrolling = <n>Plots a rolling average over n buckets (2–52) instead of raw values.
cumulativecumulative = trueAccumulates a running total; only valid for additive outputs.
stackstack = <name>Stacking for stacked_bar and area_chart: one of none, normal, percent.
trendtrend = trueOverlays a linear trend line.
annotationsannotations = trueMarks notable points on the series.
sparklinesparkline = trueAttaches a small chart image to a table render.
comparecompare = previous_periodOverlays the equally long period immediately before the window.
formatformat = (<output> = <kind>, …)Overrides an output's inferred display kind: one of count, decimal, percent, duration, ratio, text, timestamp.

Every name inside WITH (…) must be one of the query’s own outputs or groupings. A name the SELECT does not produce is rejected when the query compiles rather than silently plotting nothing.

Kind Requires
bar_chart, stacked_bar y; optionally orientation
line_chart, area_chart y; a DATE_TRUNC grouping for a time axis
donut_chart exactly one y, and few rows
scatter_chart one x and one y, both SELECTed; optionally size
heatmap value, series, and exactly two groupings
radar_chart three to eight y outputs over one grouping
kpi_card no GROUP BY — one grand-total row
bump_chart one y, a DATE_TRUNC grouping, plus one more grouping to rank
calendar_heatmap one y and DATE_TRUNC('day', …) as the only grouping
histogram one numeric bucket grouping and one count y
box_plot exactly five y outputs — see below
leaderboard optionally mentions

compare = previous_period re-runs the same aggregation over the equally long span immediately before the query’s window and overlays it, giving every point a baseline, an absolute delta, and a percentage delta.

It needs two things, and says so if either is missing: a stated time window to take the preceding period of, and a DATE_TRUNC (or patch) grouping to line the two runs up bucket by bucket.

SELECT DATE_TRUNC('week', game_creation_at) AS week,
COUNT(*) FILTER (WHERE win) AS wins,
COUNT(*) FILTER (WHERE NOT win) AS losses
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 90 DAY
GROUP BY DATE_TRUNC('week', game_creation_at)
ORDER BY week ASC
RENDER stacked_bar WITH (y = (wins, losses), compare = previous_period)

previous_period is the only comparison in the language.

A box plot takes its five numbers explicitly, and their order in y is the encoding: minimum, first quartile, median, third quartile, maximum.

SELECT MIN(kills) AS low,
QUANTILE_CONT(kills, 0.25) AS q1,
MEDIAN(kills) AS med,
QUANTILE_CONT(kills, 0.75) AS q3,
MAX(kills) AS high
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY champion
ORDER BY med DESC
LIMIT 6
RENDER box_plot WITH (y = (low, q1, med, q3, high))

The names are yours; the positions are not. Nothing is inferred from what a column is called, which is the same bargain the rest of the language makes — q3 could as easily be a 95th percentile, and only the query knows.

Each sample below is produced by compiling the query shown and running it through Scout’s actual report renderer — the same function that builds what the bot posts. They are regenerated by bun run generate, so they cannot drift from the renderer.

table

SELECT COUNT(*) AS games, AVG(win::INT) AS win_rate, kda() AS kda
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY player
ORDER BY games DESC
LIMIT 6
RENDER table
Posted to Discord
**Most games played**
```
Player | Games | Win Rate | Kda
--- | --- | --- | ---
Faker | 146 | 57.5% | 4.12
Caps | 111 | 51.4% | 3.48
Chovy | 90 | 54.4% | 3.91
Ruler | 89 | 51.7% | 3.36
Keria | 76 | 46.1% | 2.87
Zeus | 71 | 53.5% | 3.02
```

list

SELECT COUNT(*) AS games, AVG(win::INT) AS win_rate
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY player
ORDER BY games DESC
LIMIT 6
RENDER list
Posted to Discord
**Most games played**
- Faker: Games: 146, Win Rate: 57.5%
- Caps: Games: 111, Win Rate: 51.4%
- Chovy: Games: 90, Win Rate: 54.4%
- Ruler: Games: 89, Win Rate: 51.7%
- Keria: Games: 76, Win Rate: 46.1%
- Zeus: Games: 71, Win Rate: 53.5%

leaderboard

SELECT COUNT(*) AS games, AVG(win::INT) AS win_rate
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY player
ORDER BY games DESC
LIMIT 6
RENDER leaderboard WITH (mentions = 0)
Posted to Discord
**Most games played**
1. Faker — Games: 146, Win Rate: 57.5%
2. Caps — Games: 111, Win Rate: 51.4%
3. Chovy — Games: 90, Win Rate: 54.4%
4. Ruler — Games: 89, Win Rate: 51.7%
5. Keria — Games: 76, Win Rate: 46.1%
6. Zeus — Games: 71, Win Rate: 53.5%

bar_chart

SELECT COUNT(*) AS games
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY player
ORDER BY games DESC
LIMIT 6
RENDER bar_chart WITH (y = games)
A bar chart rendered by Scout: Most games played.

line_chart

SELECT DATE_TRUNC('week', game_creation_at) AS week, COUNT(*) AS games
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY DATE_TRUNC('week', game_creation_at)
ORDER BY week ASC
RENDER line_chart WITH (y = games, x_axis = 'Week', smooth = true)
A line chart rendered by Scout: Weekly game volume.

area_chart

SELECT DATE_TRUNC('week', game_creation_at) AS week, COUNT(*) AS games
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY DATE_TRUNC('week', game_creation_at)
ORDER BY week ASC
RENDER area_chart WITH (y = games, x_axis = 'Week', smooth = true)
A area chart rendered by Scout: Weekly game volume.

stacked_bar

SELECT DATE_TRUNC('week', game_creation_at) AS week, COUNT(*) FILTER (WHERE win) AS wins, COUNT(*) FILTER (WHERE NOT win) AS losses
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY DATE_TRUNC('week', game_creation_at)
ORDER BY week ASC
RENDER stacked_bar WITH (y = (wins, losses))
A stacked bar rendered by Scout: Weekly wins and losses.

donut_chart

SELECT COUNT(*) AS games
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY queue
ORDER BY games DESC
RENDER donut_chart WITH (y = games)
A donut chart rendered by Scout: Queue mix.

scatter_chart

SELECT SUM(total_damage_dealt_to_champions) AS damage, kda() AS kda, COUNT(*) AS games
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY player
RENDER scatter_chart WITH (x = damage, y = kda, size = games)
A scatter chart rendered by Scout: Damage against KDA.

heatmap

SELECT AVG(win::INT) AS win_rate
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY champion, team_position
RENDER heatmap WITH (value = win_rate, series = team_position, palette = gold)
A heatmap rendered by Scout: Champion win rate by position.

radar_chart

SELECT AVG(win::INT) AS win_rate, kda() AS kda, per_minute(creep_score) AS cs_per_minute, AVG(vision_score) AS vision_score, AVG(total_damage_dealt_to_champions) AS damage
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY player
LIMIT 3
RENDER radar_chart WITH (y = (win_rate, kda, cs_per_minute, vision_score, damage))
A radar chart rendered by Scout: Combat profile.

kpi_card

SELECT COUNT(*) AS games, AVG(win::INT) AS win_rate, kda() AS kda, AVG(game_duration_seconds) AS avg_length
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
RENDER kpi_card WITH (y = (games, win_rate, kda, avg_length))
A kpi card rendered by Scout: Server snapshot.

bump_chart

SELECT COUNT(*) AS games
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 28 DAY
GROUP BY player, DATE_TRUNC('week', game_creation_at)
ORDER BY games DESC
RENDER bump_chart WITH (y = games)
A bump chart rendered by Scout: Weekly standings.

calendar_heatmap

SELECT COUNT(*) AS games
FROM match_participants
WHERE (game_creation_at AT TIME ZONE 'UTC')::DATE BETWEEN '2026-06-29' AND '2026-07-26'
GROUP BY DATE_TRUNC('day', game_creation_at)
ORDER BY games DESC
RENDER calendar_heatmap WITH (y = games, palette = gold)
A calendar heatmap rendered by Scout: Daily activity.

histogram

SELECT FLOOR(game_duration_seconds / 300) * 300 AS bucket, COUNT(*) AS games
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY FLOOR(game_duration_seconds / 300) * 300
ORDER BY bucket ASC
RENDER histogram WITH (x = bucket, y = games)
A histogram rendered by Scout: Game length distribution.

box_plot

SELECT MIN(kills) AS low, QUANTILE_CONT(kills, 0.25) AS q1, MEDIAN(kills) AS med, QUANTILE_CONT(kills, 0.75) AS q3, MAX(kills) AS high
FROM match_participants
WHERE game_creation_at >= CURRENT_TIMESTAMP - INTERVAL 30 DAY
GROUP BY champion
ORDER BY med DESC
LIMIT 6
RENDER box_plot WITH (y = (low, q1, med, q3, high))
A box plot rendered by Scout: Kills per game by champion.

Scout is an independent League of Legends companion.

Scout isn’t endorsed by Riot Games and doesn’t reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games and all associated properties are trademarks or registered trademarks of Riot Games, Inc.