Building a Reusable Library of Dashboard Blocks
This guide demonstrates how to transform your existing dashboard charts into reusable components for your organization's Block Library. By creating a well-structured library of blocks, you empower dashboard builders to easily discover, implement, and customize sophisticated visualizations with minimal effort.
Why Create a Block Library?
Your Block Library will:
- Provide consistent visualization patterns across your organization.
- Reduce duplication of effort in dashboard creation.
- Enable less technical users to create sophisticated dashboards.
- Establish governance over analytical presentation standards.
Dashboard builders can now:
- Browse the Block Library, filter by category, and search for relevant blocks.
- Add your blocks to their dashboards with a few clicks.
- Customize parameters through an intuitive interface.
- Create professional visualizations without writing complex code.
Here's what the experience looks like for dashboard builders using your library blocks:
Add a Block to the Library
The quickest way to create a library block is directly from your canvas dashboard.
Using the Visual Editor
- Hover over the block you want to add to the library.
- Click the ... menu icon in the top-right corner of the block.
- Select Add to Block Library.
- Fill in the details:
- Title (required): A clear, descriptive name for your block.
- Description (optional): Explain what insights this visualization provides.
- Thumbnail URL (optional): A custom preview image to help users identify your block.
- Group (optional): Categorize your block for easier discovery.
- Click Confirm.
Once added, your block automatically appears in the library and becomes available to all dashboard builders.
What happens under the hood
When you add a block to the library through the UI, Holistics automatically refactors your chart into a reusable function and registers it with the @template decorator. The function is created under the library/blocks folder in your project:
├── library
│ └── blocks
│ └── mrr_pop_growth.block.aml <-- Your reusable block is created here
├── settings
├── models
│ └── model_1.model.aml
├── datasets
│ └── dataset_1.dataset.aml
└── dashboards
└── saas.page.aml
Before: Chart logic directly in the dashboard
Dashboard saas {
block v1: VizBlock {
// Chart logic here
}
}
After: Automatically generated when you add to library
@template(
title = "MRR & PoP Growth", // The title you entered
description = "How does your revenue evolve over time and compare to last year?", // Your description
thumbnail = "https://link.to/image.png" // Your thumbnail URL (if provided)
)
Func mrr_breakdown() {
VizBlock {
// Chart logic moved here
}
}
Dashboard saas {
// Reference the function in your dashboard
block v1: mrr_breakdown()
}
This separation creates a clean boundary between the chart's implementation and its usage. The @template decorator transforms an ordinary function into a discoverable, reusable block.
Create Parameterizable Library Blocks
At this point, your block is ready to use. Other dashboard builders can add it to their dashboards immediately.
However, the block currently uses the same data and settings as the original chart. If you want to make it more flexible and allow users to customize datasets, fields, or visual properties, you can add parameters.
Define parameters
To make your block adaptable for different use cases, replace hardcoded values with parameters. You can use any of the AML Types for parameters.
// Before: Hardcoded values
Func mrr_breakdown() {
VizBlock {
// ...
}
}
// After: Parameterized for flexibility
Func mrr_breakdown(
dataset: Dataset,
mrr_amount: VizFieldRef,
break_down_dimension: VizFieldRef,
date_field: VizFieldRef,
comparison_period: Number
) {
VizBlock {
// ...
}
}
Provide default values for parameters to ensure the block works out of the box. When users add it to their dashboards, the block will just work with the default values, without them having to configure anything.
// With default values
Func mrr_breakdown(
dataset: Dataset = saas_dataset,
mrr_amount: VizFieldRef = r(mrr_transactions.mrr_amount), // a metric in a model
break_down_dimension: VizFieldRef = r(mrr_transactions.subscription_type), // a dimension in a model
date: VizFieldRef = r(mrr_transactions.subscription_type), // a dimension in a model
comparison_period: Number = 12
) {
VizBlock {
// ...
}
}
When dashboard builders edit a block, they will see the default values you provided. This ensures a smooth experience for users who want to use the block without any configuration.
Parameter best practices:
- Provide sensible defaults where appropriate.
- Consider what aspects users will most likely want to customize.
- Balance flexibility with simplicity. Too many parameters can be overwhelming.
Implement parameter references
Once you've defined parameters, update your function to actually use them. Use string interpolation to incorporate parameters in AQL field definitions.
Func mrr_breakdown(
dataset: Dataset = saas_dataset,
mrr_amount: VizFieldRef = r(mrr_transactions.mrr_amount),
break_down_dimension: VizFieldRef = r(mrr_transactions.subscription_type),
date: VizFieldRef = r(mrr_transactions.subscription_type),
comparison_period: Number = 12
) {
VizBlock {
viz: CombinationChart {
// Use dataset parameter
dataset: dataset,
calculation metric {
// Use mrr_amount parameter in field formula with string interpolation
formula: @aql sum(${mrr_amount.model}.${mrr_amount.field}),
}
legend: VizFieldFull {
// Use break_down_dimension parameter
ref: break_down_dimension
format {
type: 'text'
}
}
filter {
// Use other parameters
field: date
operator: 'last'
value: comparison_period
modifier: 'month'
}
}
}
}
Here are some values you might want to make customizable:
- Dataset names.
- Field references.
- Field formulas.
- Filter values.
- Visual properties (colors, labels, etc.).
Enhance Block Metadata
Once you've added parameters, your block is fully functional. For larger teams or complex block libraries, you may want to add metadata that helps users discover and understand your blocks more easily.
@template(
title = "MRR & PoP Growth",
description = "Visualize monthly recurring revenue with period-over-period comparison",
thumbnail = "https://link.to/image.png",
metadata = {
// Categorize your block for easier discovery
group: 'SaaS Metrics',
// Specify default dimensions when added to a dashboard
block_width: 620,
block_height: 460,
// Document each parameter
func: TemplateFuncMetadata {
param dataset: GeneralParamMetadata {
description: 'The dataset containing your revenue information'
},
param mrr_amount: GeneralParamMetadata {
description: 'The field containing monthly recurring revenue values'
},
param date_field: GeneralParamMetadata {
description: 'The date field to use for time series analysis'
},
// Useful when a block needs color customization, Holistics will display a color picker
// for dashboard builders so that they don't have to input HEX code
param third_bin_color: StringParamMetadata {
input_type: 'color-picker'
}
}
}
)
Effective metadata includes:
- Grouping: Logical categorization for easier discovery.
- Size specifications: Default dimensions when added to a dashboard.
- Parameter documentation: Clear descriptions of each parameter's purpose.
- Input types: Special input widgets like color pickers for better user experience.
Best Practices
- Test your block. Before publishing to the library, test your block with various parameter combinations to ensure it behaves as expected in all scenarios.
- Plan for maintenance. Consider how you'll handle updates to your blocks. Major changes might warrant a new block rather than modifying an existing one that's already in use.
- Document beyond code. Maintain comprehensive documentation about your block library, including when to use specific blocks, common customization patterns, and real-world examples.