Documentation Index
Fetch the complete documentation index at: https://mintlify.com/evidence-dev/evidence/llms.txt
Use this file to discover all available pages before exploring further.
Loops and Conditionals
Evidence lets you programmatically control what content appears on your pages using loops and conditional statements. This enables dynamic, data-driven reports that adapt to your data.
Each Loops
Loops let you iterate over query results and create repeating content for each row.
Basic Loop Syntax
{#each query_name as alias}
{alias.column_name}
{/each}
query_name: The query result to loop over
alias: A name you choose to reference the current row
- Access columns with
{alias.column_name}
Simple Loop Example
```sql top_products
SELECT
product_name,
sales,
units_sold
FROM products
ORDER BY sales DESC
LIMIT 5
```
## Top 5 Products
{#each top_products as product}
### {product.product_name}
- Sales: <Value data={product} column="sales" fmt="usd"/>
- Units Sold: <Value data={product} column="units_sold" fmt="num0"/>
{/each}
Output:
Top 5 Products
Laptop
- Sales: $45,000
- Units Sold: 150
Monitor
- Sales: $32,000
- Units Sold: 320
…
Loops with Components
Create repeated visualizations:
```sql cities
SELECT DISTINCT city
FROM sales
ORDER BY city
```
{#each cities as location}
## Sales in {location.city}
```sql city_sales_{location.city}
SELECT
DATE_TRUNC('month', date) as month,
SUM(revenue) as revenue
FROM sales
WHERE city = '${location.city}'
GROUP BY month
ORDER BY month
```
<LineChart
data={city_sales_{location.city}}
x="month"
y="revenue"
/>
{/each}
Query names inside loops must be unique. The example above would fail because each iteration creates the same query name. Use templated pages instead for this use case.
Better Alternative: Loops for Lists
```sql city_summary
SELECT
city,
SUM(sales) as total_sales,
COUNT(*) as order_count
FROM sales
GROUP BY city
ORDER BY total_sales DESC
```
{#each city_summary as city}
- **{city.city}**: <Value data={city} column="total_sales" fmt="usd"/> from <Value data={city} column="order_count"/> orders
{/each}
Loops in Components
Generate component children dynamically:
```sql categories
SELECT DISTINCT category
FROM orders
ORDER BY category
```
<Accordion>
{#each categories as cat}
<AccordionItem title={cat.category}>
Content for the {cat.category} category.
</AccordionItem>
{/each}
</Accordion>
Conditionals (If/Else)
Conditionals control what content displays based on data conditions.
Basic Conditional Syntax
{#if condition}
Content when condition is true
{:else}
Content when condition is false
{/if}
Simple Conditional Example
```sql sales_target
SELECT
100000 as target,
125000 as actual
```
{#if sales_target[0].actual >= sales_target[0].target}
## Target Achieved! 🎉
Congratulations! We exceeded our sales target.
{:else}
## Below Target
We need to increase sales to meet our target.
{/if}
Multiple Conditions
{#if condition_1}
First condition is true
{:else if condition_2}
Second condition is true
{:else}
No conditions are true
{/if}
Practical Example: Empty State Handling
```sql failed_orders
SELECT *
FROM orders
WHERE status = 'failed'
```
{#if failed_orders.length > 0}
## Failed Orders
<Warning>
There are {failed_orders.length} failed orders that need attention.
</Warning>
<DataTable data={failed_orders} />
{:else}
<Info>
No failed orders - everything is running smoothly!
</Info>
{/if}
Conditional Alerts Based on Thresholds
```sql performance_metrics
SELECT
0.15 as error_rate,
0.05 as warning_threshold,
0.10 as critical_threshold
```
{#if performance_metrics[0].error_rate > performance_metrics[0].critical_threshold}
<Alert status=error>
Critical: Error rate is at {performance_metrics[0].error_rate}%
</Alert>
{:else if performance_metrics[0].error_rate > performance_metrics[0].warning_threshold}
<Alert status=warning>
Warning: Error rate is elevated at {performance_metrics[0].error_rate}%
</Alert>
{:else}
<Alert status=success>
System performing normally. Error rate: {performance_metrics[0].error_rate}%
</Alert>
{/if}
Conditional Components
```sql metrics
SELECT
COUNT(*) as total_records
FROM large_dataset
```
{#if metrics[0].total_records > 1000}
<Warning>
Dataset is large. Showing summary statistics instead of full table.
</Warning>
```sql summary
SELECT
AVG(value) as avg_value,
MAX(value) as max_value,
MIN(value) as min_value
FROM large_dataset
```
<DataTable data={summary} />
{:else}
<DataTable data={large_dataset} />
{/if}
Combining Loops and Conditionals
Use loops and conditionals together for complex logic:
```sql team_performance
SELECT
team_name,
sales,
quota,
(sales::float / quota) as attainment
FROM teams
ORDER BY attainment DESC
```
{#each team_performance as team}
## {team.team_name}
{#if team.attainment >= 1.0}
✅ **Exceeding quota**: <Value data={team} column="attainment" fmt="pct"/> attainment
{:else if team.attainment >= 0.8}
⚠️ **On track**: <Value data={team} column="attainment" fmt="pct"/> attainment
{:else}
❌ **Below target**: <Value data={team} column="attainment" fmt="pct"/> attainment
{/if}
---
{/each}
Conditional Table Columns
Show different columns based on user input:
```sql summary
SELECT
category,
SUM(sales) as sales,
COUNT(*) as orders,
AVG(sales) as avg_order_value
FROM orders
GROUP BY category
```
<Dropdown name=metric>
<DropdownOption value="sales">Sales</DropdownOption>
<DropdownOption value="orders">Orders</DropdownOption>
<DropdownOption value="aov">Avg Order Value</DropdownOption>
</Dropdown>
<DataTable data={summary}>
<Column id="category" />
{#if inputs.metric.value === 'sales'}
<Column id="sales" fmt="usd0k" />
{:else if inputs.metric.value === 'orders'}
<Column id="orders" fmt="num0" />
{:else}
<Column id="avg_order_value" fmt="usd2" />
{/if}
</DataTable>
Common Conditional Patterns
Check Query Length
{#if query_name.length === 0}
No data available
{:else}
<DataTable data={query_name} />
{/if}
Compare Values
{#if current_value > previous_value}
📈 Trending up
{:else}
📉 Trending down
{/if}
Check for Null/Undefined
{#if data[0]?.value}
<Value data={data} column="value" />
{:else}
No value available
{/if}
Date Comparisons
{#if new Date(last_update[0].date) < new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)}
⚠️ Data is more than a week old
{/if}
JavaScript Expressions
Evidence supports JavaScript expressions inside { }:
{2 + 2}
<!-- Output: 4 -->
{orders.length} orders
<!-- Output: 42 orders -->
{(revenue[0].total / 1000).toFixed(1)}k
<!-- Output: 125.5k -->
{new Date().toLocaleDateString()}
<!-- Output: 3/4/2026 -->
When to Use Loops vs. Templated Pages
Use Loops when:
- Creating a list or small number of repeated sections on one page
- You don’t need unique URLs for each item
- Content is simple (headings, values, small charts)
Use Templated Pages when:
- Each item deserves its own page with a unique URL
- You have many items (>20)
- Each item has substantial content
- You want shareable links to individual items
For large datasets, prefer DataTables with search/filter instead of loops to maintain performance.
Loops execute on every row. For large datasets (1000+ rows), avoid:
- Creating charts in loops
- Running queries inside loops
- Complex calculations per row
Instead, use DataTables or pre-aggregate data in SQL.
Debugging Tips
View Query Data
Inspect what data is available:
{JSON.stringify(query_name, null, 2)}
Check Array Length
Query returned {query_name.length} rows
Conditional Debugging
{#if query_name.length === 0}
⚠️ Debug: Query returned no results
{/if}