Parametric TickFormat - Custom Axis Formatting
Observable Plot's tickFormat option accepts both d3-format strings and JavaScript functions for customizing axis tick labels. The dg-plot plugin supports parametric functions as strings, allowing complex formatting logic directly in your configuration.
Overview
Use parametric tickFormat to:
- Convert decimals to percentages
- Format currency values
- Abbreviate large numbers (K, M, B)
- Apply conditional formatting
- Create custom date/time labels
- Round or truncate values
Basic Usage
Percentage Formatting
Convert decimal values (0-1) to percentages (0-100%):
type: line
data:
source: '[{"year": 2020, "adoption": 0.25}, {"year": 2021, "adoption": 0.42}, {"year": 2022, "adoption": 0.68}]'
x: year
y: adoption
scales:
y:
tickFormat: '(d) => Math.round(d * 100) + "%"'Result: Y-axis shows "25%", "42%", "68%"
Currency Formatting
Format numbers as USD currency:
type: bar
data:
source: '[{"quarter": "Q1", "revenue": 125000}, {"quarter": "Q2", "revenue": 187500}, {"quarter": "Q3", "revenue": 234000}]'
x: quarter
y: revenue
scales:
y:
tickFormat: '(d) => "$" + d.toLocaleString()'Result: Y-axis shows "$125,000", "$187,500", "$234,000"
Number Abbreviation
Abbreviate large numbers with K (thousands) and M (millions):
type: bar
data:
source: '[{"company": "A", "users": 1500}, {"company": "B", "users": 425000}, {"company": "C", "users": 2300000}]'
x: company
y: users
scales:
y:
tickFormat: |
(d) => {
if (d >= 1e6) return (d / 1e6).toFixed(1) + "M";
if (d >= 1e3) return (d / 1e3).toFixed(1) + "K";
return d;
}Result: Y-axis shows "1.5K", "425.0K", "2.3M"
Template Literals
Use template literals for flexible formatting:
type: scatter
data:
source: '[{"day": 1, "temp": 72}, {"day": 2, "temp": 68}, {"day": 3, "temp": 75}]'
x: day
y: temp
scales:
y:
tickFormat: '(d) => `${d}°F`'Result: Y-axis shows "72°F", "68°F", "75°F"
Conditional Formatting
Apply different formats based on value ranges:
type: area
data:
source: '[{"month": "Jan", "sales": 45}, {"month": "Feb", "sales": 120}, {"month": "Mar", "sales": 80}]'
x: month
y: sales
scales:
y:
tickFormat: '(d) => d > 100 ? "High: $" + d : "Low: $" + d'Result: Y-axis shows "Low: $45", "High: $120", "Low: $80"
Real-World Examples
1. Market Share Dashboard
Display percentages for market share analysis:
type: bar
data:
source: '[{"vendor": "A", "share": 0.32}, {"vendor": "B", "share": 0.28}, {"vendor": "C", "share": 0.25}, {"vendor": "D", "share": 0.15}]'
x: vendor
y: share
scales:
y:
domain: [0, 1]
tickFormat: '(d) => (d * 100).toFixed(0) + "%"'2. Financial Data with Currency
Format stock prices with proper locale and precision:
type: line
data:
source: '[{"date": "2024-01", "price": 124.50}, {"date": "2024-02", "price": 128.75}, {"date": "2024-03", "price": 132.10}]'
x: date
y: price
scales:
y:
tickFormat: '(d) => "$" + d.toFixed(2)'3. Large-Scale Analytics
Handle millions of records with abbreviated notation:
type: bar
data:
source: '[{"region": "US", "impressions": 5400000}, {"region": "EU", "impressions": 2100000}, {"region": "APAC", "impressions": 890000}]'
x: region
y: impressions
scales:
y:
tickFormat: |
(d) => {
if (d >= 1e9) return (d / 1e9).toFixed(1) + "B";
if (d >= 1e6) return (d / 1e6).toFixed(1) + "M";
if (d >= 1e3) return (d / 1e3).toFixed(1) + "K";
return d.toFixed(0);
}4. Survey Results with Precision
Display survey scores with decimal precision:
type: area
data:
source: '[{"q": "Q1", "score": 3.42}, {"q": "Q2", "score": 3.87}, {"q": "Q3", "score": 4.12}]'
x: q
y: score
scales:
y:
domain: [0, 5]
tickFormat: '(d) => d.toFixed(1)'5. Time-Based Metrics
Format duration/time values:
type: line
data:
source: '[{"day": 1, "duration": 245}, {"day": 2, "duration": 312}, {"day": 3, "duration": 189}]'
x: day
y: duration
scales:
y:
tickFormat: |
(d) => {
const mins = Math.floor(d / 60);
const secs = d % 60;
return mins + "m " + secs + "s";
}Result: Y-axis shows "4m 5s", "5m 12s", "3m 9s"
Advanced Patterns
Locale-Aware Formatting
Use native JavaScript locale features:
scales:
y:
tickFormat: |
(d) => new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 0
}).format(d)Scientific Notation
For very large or very small numbers:
scales:
y:
tickFormat: '(d) => d.toExponential(2)'Conditional with Threshold
Highlight values above/below thresholds:
scales:
y:
tickFormat: |
(d) => {
if (d > 1000) return "🔴 " + d.toFixed(0);
if (d > 500) return "🟡 " + d.toFixed(0);
return "🟢 " + d.toFixed(0);
}Function Syntax Guide
Single-Line Expressions
tickFormat: '(d) => d * 100 + "%"'
tickFormat: '(d) => "$" + d.toLocaleString()'Template Literals
tickFormat: '(d) => `${d.toFixed(2)}x`'
tickFormat: '(d) => `Value: ${d > 100 ? "high" : "low"}`'Multi-Line Functions
tickFormat: |
(d) => {
if (d >= 1e6) return (d / 1e6).toFixed(1) + "M";
if (d >= 1e3) return (d / 1e3).toFixed(1) + "K";
return d.toFixed(0);
}Without Parentheses
For single parameters, parentheses are optional:
tickFormat: 'd => d * 2'Security Considerations
- No Global Scope Access: Functions are evaluated in isolation without access to outer scope variables
- Safe Evaluation: Uses JavaScript
Functionconstructor (noteval) - No Side Effects: Formatters should only return formatted strings, not modify data
- Validation: Invalid functions fall back to default formatting with a warning
Performance Tips
- Keep Formatting Simple: Complex calculations are executed for every tick label
- Cache Computed Values: Pre-compute constants outside the function body
- Avoid Loops: For range-based formatting, use conditional logic instead
- Test Edge Cases: Ensure formatters handle minimum/maximum axis values
Combining with Other Scale Options
Parametric tickFormat works alongside other scale configuration:
scales:
y:
type: linear
domain: [0, 100]
nice: true
tickFormat: '(d) => d + "%"'
label: "Completion Rate"Fallback to d3-Format Strings
If you prefer d3-format syntax, it's still supported:
scales:
y:
tickFormat: ',d' # Thousand separatorsThe plugin automatically detects arrow functions vs. d3-format strings.
Common Pitfalls
Forgetting Return: Multi-line functions must have explicit
returnyaml# ❌ Wrong tickFormat: '(d) => { d * 100 }' # ✅ Correct tickFormat: '(d) => { return d * 100; }'Unescaped Template Literals: Quote properly in YAML
yaml# ❌ Wrong tickFormat: (d) => `${d}%` # ✅ Correct tickFormat: '(d) => `${d}%`'Assuming Data Types: Verify input data types before formatting
yaml# ✅ Safe - handles null/undefined tickFormat: '(d) => d != null ? d.toFixed(2) : "N/A"'