PBIR Template Extractor
Extract existing PBIR visuals and convert them to reusable templates with placeholders.
When to Use This Skill
Use when the user wants to:
- •Save an existing visual as a reusable template
- •Create a new template type not in the library
- •Standardize a visual configuration for reuse
- •Extract a well-formatted visual from Power BI Desktop
- •Add to the visual-templates library
Workflow
Step 1: Locate Source Visual
Find the visual.json file to extract:
# List pages in the report Glob: .Report/definition/pages/*/page.json # List visuals on a page Glob: .Report/definition/pages/[PageGUID]/visuals/*/visual.json # Read specific visual Read: .Report/definition/pages/[PageGUID]/visuals/[VisualGUID]/visual.json
Step 2: Analyze Visual Structure
Identify:
- •Visual type -
visualTypeproperty (card, barChart, lineChart, etc.) - •Data roles - Query projections (Values, Category, Y, Series, etc.)
- •Formatting - Objects in
visualContainerObjectsandvisual.objects - •Special features - Interactions, drill-through, conditional formatting
Step 3: Create Placeholders
Replace specific values with {{PLACEHOLDER}} syntax:
Always Replace:
| Value | Placeholder |
|---|---|
| Visual GUID (name) | {{VISUAL_NAME}} |
| Position x | {{X}} |
| Position y | {{Y}} |
| Position z | {{Z}} |
| Width | {{WIDTH}} |
| Height | {{HEIGHT}} |
| Tab order | {{TAB_ORDER}} |
| Title text | {{TITLE}} |
| Filter names (GUIDs) | {{FILTER_GUID}} |
Data Binding Replacements:
| Binding | Placeholders |
|---|---|
| Primary measure | {{TABLE_NAME}}, {{MEASURE_NAME}} |
| Multiple measures | {{MEASURE_1_TABLE}}, {{MEASURE_1_NAME}}, etc. |
| Category axis | {{CATEGORY_TABLE}}, {{CATEGORY_COLUMN}} |
| Series/Legend | {{SERIES_TABLE}}, {{SERIES_COLUMN}} |
| Matrix rows | {{ROW_TABLE}}, {{ROW_COLUMN}} |
| Matrix columns | {{COLUMN_TABLE}}, {{COLUMN_COLUMN}} |
| Map location | {{LOCATION_TABLE}}, {{LOCATION_COLUMN}} |
| Scatter X/Y | {{X_MEASURE_TABLE}}, {{Y_MEASURE_TABLE}}, etc. |
Formatting Replacements (Optional):
| Property | Placeholder |
|---|---|
| Font size | {{FONT_SIZE}} |
| Display units | {{DISPLAY_UNITS}} |
| Colors | {{MEASURE_1_COLOR}}, etc. |
| Legend position | {{LEGEND_POSITION}} |
Step 4: Validate Template
Before saving, verify:
- • JSON is well-formed
- • All specific values replaced with placeholders
- • Schema reference preserved (
$schema) - • No hardcoded table/column names remain
- • No hardcoded visual names remain
Step 5: Name and Save Template
Naming Convention:
[visual-type]-[variant].json Examples: - card-single-measure.json - line-chart-with-series.json - slicer-dropdown.json - azure-map-bubble.json
Save Location:
visual-templates/[template-name].json
Step 6: Update Documentation
Add entry to visual-templates/README.md:
| `[template-name].json` | [Visual Type] | [Description] |
Document any unique placeholders for this template.
Example: Extract a Bar Chart
Source visual.json:
{
"$schema": "https://developer.microsoft.com/json-schemas/fabric/item/report/definition/visualContainer/2.4.0/schema.json",
"name": "SalesByRegionBar",
"position": {
"x": "100",
"y": "200",
"z": "2000",
"height": "300",
"width": "400",
"tabOrder": "1"
},
"visual": {
"visualType": "barChart",
"query": {
"queryState": {
"Category": {
"projections": [
{
"field": {
"Column": {
"Expression": { "SourceRef": { "Entity": "dim_region" } },
"Property": "region_name"
}
},
"queryRef": "dim_region.region_name",
"nativeQueryRef": "region_name",
"active": true
}
]
},
"Y": {
"projections": [
{
"field": {
"Measure": {
"Expression": { "SourceRef": { "Entity": "Fact_Sales" } },
"Property": "Total Sales"
}
},
"queryRef": "Fact_Sales.Total Sales",
"nativeQueryRef": "Total Sales"
}
]
}
}
}
}
}
Extracted template:
{
"$schema": "https://developer.microsoft.com/json-schemas/fabric/item/report/definition/visualContainer/2.4.0/schema.json",
"name": "{{VISUAL_NAME}}",
"position": {
"x": "{{X}}",
"y": "{{Y}}",
"z": "{{Z}}",
"height": "{{HEIGHT}}",
"width": "{{WIDTH}}",
"tabOrder": "{{TAB_ORDER}}"
},
"visual": {
"visualType": "barChart",
"query": {
"queryState": {
"Category": {
"projections": [
{
"field": {
"Column": {
"Expression": { "SourceRef": { "Entity": "{{CATEGORY_TABLE}}" } },
"Property": "{{CATEGORY_COLUMN}}"
}
},
"queryRef": "{{CATEGORY_TABLE}}.{{CATEGORY_COLUMN}}",
"nativeQueryRef": "{{CATEGORY_COLUMN}}",
"active": true
}
]
},
"Y": {
"projections": [
{
"field": {
"Measure": {
"Expression": { "SourceRef": { "Entity": "{{TABLE_NAME}}" } },
"Property": "{{MEASURE_NAME}}"
}
},
"queryRef": "{{TABLE_NAME}}.{{MEASURE_NAME}}",
"nativeQueryRef": "{{MEASURE_NAME}}"
}
]
}
}
}
}
}
Handling Complex Visuals
Multiple Measures
For visuals with multiple Y values, use numbered placeholders:
{{MEASURE_1_TABLE}}, {{MEASURE_1_NAME}}
{{MEASURE_2_TABLE}}, {{MEASURE_2_NAME}}
{{MEASURE_3_TABLE}}, {{MEASURE_3_NAME}}
Conditional Formatting
Keep conditional formatting rules but placeholder the references:
"rules": [
{
"field": {
"Measure": {
"Expression": { "SourceRef": { "Entity": "{{TABLE_NAME}}" } },
"Property": "{{MEASURE_NAME}}"
}
}
}
]
Default Values
Some formatting values should remain hardcoded (good defaults):
- •Font family: Keep as-is (e.g., "Segoe UI")
- •Default colors: Keep unless user wants custom
- •Axis show/hide: Keep as-is
Quality Checklist
Before finalizing template:
- •
$schemaproperty preserved at top - •
name→{{VISUAL_NAME}} - • All position values → placeholders
- • All table names → placeholders
- • All column/measure names → placeholders
- • Filter GUIDs →
{{FILTER_GUID}} - • Title text →
{{TITLE}} - • JSON validates successfully
- • Template follows naming convention
Contribution to Template Library
When adding new templates:
- •Test the template by creating a visual with it
- •Verify it opens correctly in Power BI Desktop
- •Add to
visual-templates/README.md - •Document any unique placeholders
- •Add to the "Available Templates" table
Constraints
- •Never modify the source visual.json
- •Always preserve the schema version
- •Use consistent placeholder naming
- •Test templates before adding to library