When to use pie
Pie is the right choice for part-of-whole composition when the number of slices is small (typically ≤ 6) and proportional weight matters. Use the donut variant when you want to free the chart center for an inner label or a related KPI; use the rose variant for an alternative encoding where slice angle is fixed but radius reflects the value.
Use bar with chart.stack: percent instead when the categories are many, or when you want to compare composition across multiple groups. Use grid when the audience needs the actual percentages in tabular form.
Mapping
mapping.x— required. String field used as the slice name and the legend label.mapping.y— required. Numeric field that drives the slice angle.
mapping:
x: status
y: order_count
Slice colors come from the platform palette in query order. To control order, sort in the underlying Malloy query.
chart shortcuts
The chart block is typed and closed.
chart.variant—"pie"(default),"donut", or"rose". Donut adds an inner radius (free center); rose switches to a rose-area layout where slice angle is uniform and radius encodes the value.chart.inner_radius— number (pixels) or string (e.g."55%"). Use withvariant: donut; the larger the inner radius, the thinner the donut ring.chart.outer_radius— number or string.chart.slice_border_radius— pixels. Rounded corners on slices for a softer look.chart.show_value_labels— boolean. Turns slice labels on. Style them withchart.labelbelow.chart.cross_filter— boolean, defaulttrue.chart.height— pixel height of the viz container.
Pass-through fields (1:1 with the underlying chart):
chart.center— center of the pie. Array[x, y]of percent strings (e.g.["50%", "50%"]) or pixel numbers.chart.start_angle— degrees. Default90.chart.min_angle— degrees. Minimum angle for tiny slices to remain visible.chart.rose_type— when using a rose layout:"radius"or"area".
Slice labels
chart.label is an object that styles slice labels when chart.show_value_labels is on:
position—"inside","outside","top","bottom","left","right", plus the"insideLeft"/"insideRight"variants.rotate— degrees, between -90 and 90.color,font_size,font_weight.formatter— string template. Use{b}for the slice name,{c}for the value,{d}for the percentage.distance,align,vertical_align,clip.
chart:
show_value_labels: true
label:
position: outside
formatter: "{b}: {d}%"
Legend & tooltip
chart.legend controls the legend; chart.tooltip controls the hover tooltip. Both share the same shape as on bar — see that page for the full sub-block reference.
Pie-specific tip: for a donut variant with the legend on the right, set center: ["42%", "50%"] to nudge the donut left so the legend has more room.
format
format.yorformat[<value_field_name>]— pattern for slice tooltip values and inside-slice labels.formatat the root — fallback.
Cross-filter behavior
- Clicking a slice cross-filters the rest of the dashboard by the slice label.
- The clicked field (the slice-label field, e.g.
status) must be declared as a parameter in at least one model used by the dashboard, or the click is silently ignored. - Disable per viz with
chart.cross_filter: false.
See Cross-filtering for the full mechanism.
Worked examples
Donut with outside labels and bottom legend:
id: ec_orders_by_status_pie
title: Orders by Status
query: "models/ec_revenue.malloy::by_status"
type: pie
mapping:
label: status
value: order_count
chart:
variant: donut
inner_radius: "55%"
outer_radius: "78%"
show_value_labels: true
label:
position: outside
formatter: "{b}: {d}%"
legend:
show: true
position: bottom
format:
order_count: "#,##0"
published: true
Solid pie with inside labels:
type: pie
mapping:
label: category
value: revenue
chart:
show_value_labels: true
label:
position: inside
color: "#fff"
font_weight: bold
legend:
show: false
format:
revenue: "$#,##0"
Rose layout (slice angle uniform, radius encodes value):
chart:
variant: rose
rose_type: area
start_angle: 0
show_value_labels: true
Common pitfalls
- Too many slices. A pie with more than ~6 slices becomes hard to read. Sort + limit in the Malloy query, or aggregate small slices into an "Other" bucket.
- Slices with zero or near-zero values disappear. Use
chart.min_angleto enforce a minimum visible angle, or filter zeros out in the query. - Labels overlap on a tight chart. Move them inside (
chart.label.position: "inside") or rotate. - Donut inner content collides with labels. When you put a KPI in the donut center via dashboard layout, set
chart.label.position: "outside"so the slice labels do not collide. - Slice colors are not what you expected. Colors are assigned in query-result order from the platform palette. Sort the query so the largest or most important slice gets the dominant color.
- Cross-filter clicks have no effect. The slice-label field must be declared as a parameter in at least one model used by the dashboard.