Galaxy Markdown Visualizations: Complete Reference
How visualizations are embedded, rendered, and extended in Galaxy’s markdown system.
1. Overview
Galaxy Markdown supports five fenced block types, dispatched by the SectionWrapper.vue router:
| Block Tag | Handler Component | Purpose |
|---|---|---|
```galaxy | MarkdownGalaxy.vue | Dataset, job, workflow directives with function-call syntax |
```vega | MarkdownVega.vue | Inline Vega/Vega-Lite chart specs (JSON) |
```visualization | MarkdownVisualization.vue | Galaxy visualization plugin embeds (JSON config) |
```vitessce | MarkdownVitessce.vue | Spatial/single-cell data dashboards (JSON config) |
| (default) | MarkdownDefault.vue | Standard markdown via markdown-it + KaTeX math |
The first (galaxy) uses directive function-call syntax. The other three use JSON payloads parsed client-side. All visualization types support an expand/maximize button for fullscreen viewing.
2. Galaxy Directives (```galaxy blocks)
These are the structured directives — parsed by both backend (markdown_parse.py) and frontend (parse.ts). They use function-call syntax with named arguments.
Dataset Visualization Directives
| Directive | Purpose | Key Arguments |
|---|---|---|
history_dataset_display | Full dataset content | history_dataset_id, hid, input, output, invocation_id |
history_dataset_as_image | Render as image (PNG/SVG) | Same + path |
history_dataset_as_table | Render as HTML table | Same + path, title, compact, footer, show_column_headers |
history_dataset_peek | Pre-computed content preview | Same core args |
history_dataset_info | Dataset metadata | Same core args |
history_dataset_name | Just the dataset name | Same core args |
history_dataset_type | Just the file format | Same core args |
history_dataset_link | Clickable download link | Same + path, label |
history_dataset_index | File listing (composite) | Same + path |
history_dataset_embedded | Inline embed | Same core args |
history_dataset_collection_display | Collection contents | history_dataset_collection_id, hid, input, output, invocation_id |
Syntax Examples
Block directive (inside ```galaxy fence):
history_dataset_as_table(history_dataset_id=12345, title="Mapping Stats", compact=true)
Inline directive (in regular markdown text):
The alignment produced ${galaxy history_dataset_name(output="results")} with format ${galaxy history_dataset_type(output="results")}.
Workflow-relative references (resolved at render time via invocation context):
history_dataset_display(output="alignment_results", invocation_id=98765)
ID Formats
| Format | Example | Context |
|---|---|---|
| Internal (storage) | history_dataset_id=12345 | Raw DB IDs, stored in page revisions |
| Encoded (API/export) | history_dataset_id=a1b2c3d4e5f6 | Base36-encoded for API responses |
| HID (notebook-relative) | hid=3 | Accepted in validation, resolved by agent tools |
| Workflow-relative | output="bam_output" | Resolved via invocation store |
Backend Processing Pipeline
ready_galaxy_markdown_for_import()
Encoded IDs ──────────────────────────────────────────→ Internal IDs (storage)
validate_galaxy_markdown()
Markdown text ────────────────────────────────────────→ Syntax errors (line numbers)
resolve_invocation_markdown()
Workflow-relative ────────────────────────────────────→ Absolute dataset IDs
ready_galaxy_markdown_for_export()
Internal IDs ─────────────────────────────────────────→ Encoded IDs + expanded embeds
Two export handlers:
- Lazy (
ReadyForExportMarkdownDirectiveHandler): Preserves directives, collects metadata for frontend rendering. Used for interactive display. - Eager (
ToBasicMarkdownDirectiveHandler): Expands directives to standard markdown/HTML. Used for PDF export via WeasyPrint.
Frontend Rendering
MarkdownGalaxy.vue dispatches to 22 specialized element components in Markdown/Sections/Elements/:
| Component | Directive |
|---|---|
HistoryDatasetDisplay.vue | history_dataset_display |
HistoryDatasetAsImage.vue | history_dataset_as_image |
HistoryDatasetAsTable.vue | history_dataset_as_table |
HistoryDatasetDetails.vue | history_dataset_peek, history_dataset_info |
HistoryDatasetLink.vue | history_dataset_link |
HistoryDatasetIndex.vue | history_dataset_index |
HistoryDatasetCollection.vue | history_dataset_collection_display |
WorkflowDisplay.vue | workflow_display |
WorkflowImage.vue | workflow_image |
JobMetrics.vue | job_metrics |
JobParameters.vue | job_parameters |
ToolStd.vue | tool_stdout, tool_stderr |
InvocationTime.vue | invocation_time, generate_time |
3. Vega-Lite Visualizations (```vega blocks)
Self-contained chart specifications using the Vega-Lite grammar. No plugin system needed — the spec IS the chart.
Syntax
```vega
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "Gene expression bar chart",
"data": {
"values": [
{"gene": "TP53", "expression": 42.5},
{"gene": "BRCA1", "expression": 38.2},
{"gene": "EGFR", "expression": 55.1}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "gene", "type": "nominal"},
"y": {"field": "expression", "type": "quantitative"}
}
}
```
Rendering Pipeline
MarkdownVega.vue
│ JSON.parse(content)
│ Inject width: "container" for responsive sizing
▼
VegaWrapper.vue (client/src/components/Common/VegaWrapper.vue)
│ vega-embed library with SVG renderer
│ ResizeObserver for responsive width
│ Cleanup via vegaView.finalize() on unmount
▼
Rendered SVG chart
Supported Chart Types (via Vega-Lite)
Vega-Lite’s mark property supports all standard chart types:
| Mark Type | Description |
|---|---|
bar | Bar charts (vertical/horizontal) |
line | Line charts, time series |
point | Scatter plots |
area | Area charts, stacked areas |
circle | Circle plots |
square | Square plots |
rect | Heatmaps, 2D histograms |
tick | Strip plots |
boxplot | Box-and-whisker plots |
rule | Reference lines, error bars |
text | Text labels |
arc | Pie/donut charts |
geoshape | Geographic maps |
trail | Variable-width lines |
image | Image marks |
Vega-Lite also supports layered, faceted, concatenated, and repeated views for complex dashboards.
Key Properties
// VegaWrapper.vue props
interface VisSpec {
spec: VisualizationSpec; // Full Vega or Vega-Lite spec
fillWidth?: boolean; // Auto-resize to container (default: true)
}
- Renderer: SVG (not Canvas) for crisp rendering in markdown
- Responsive:
useResizeObserveron container updatesvegaView.width() - Lifecycle: Chart finalized on component unmount (no memory leaks)
Example: Scatter Plot with Color Encoding
```vega
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"},
"mark": "point",
"encoding": {
"x": {"field": "Horsepower", "type": "quantitative"},
"y": {"field": "Miles_per_Gallon", "type": "quantitative"},
"color": {"field": "Origin", "type": "nominal"},
"tooltip": [
{"field": "Name", "type": "nominal"},
{"field": "Horsepower", "type": "quantitative"},
{"field": "Miles_per_Gallon", "type": "quantitative"}
]
}
}
```
Example: Time Series
```vega
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {"url": "https://vega.github.io/vega-datasets/data/stocks.csv"},
"mark": "line",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "price", "type": "quantitative"},
"color": {"field": "symbol", "type": "nominal"}
}
}
```
4. Galaxy Plugin Visualizations (```visualization blocks)
The full plugin-based visualization framework. Plugins are external packages that run in sandboxed iframes with their own JS/CSS.
Syntax
```visualization
{
"visualization_name": "charts",
"visualization_title": "Quality Score Distribution",
"dataset_id": "a1b2c3d4e5f6",
"height": 400,
"settings": {
"chart_type": "bar",
"x_axis_label": "Quality Score",
"y_axis_label": "Count"
},
"tracks": [...]
}
```
Configuration Schema
// Parsed from JSON content by MarkdownVisualization.vue
interface VisualizationConfig {
visualization_name: string; // Plugin identifier (required)
visualization_title?: string; // Display title
dataset_id?: string; // Direct dataset reference
dataset_url?: string; // Alternative: URL to data
dataset_label?: { // Workflow-relative reference
invocation_id?: string;
input?: string; // Workflow input label
output?: string; // Workflow output label
};
dataset_name?: string; // Human-readable dataset name
height?: number; // Pixel height (default: 400)
settings?: object; // Plugin-specific settings
tracks?: object[]; // Plugin-specific data tracks
}
Rendering Pipeline
MarkdownVisualization.vue
│ JSON.parse(content)
│ Resolve dataset_label via invocationStore (if workflow context)
▼
VisualizationWrapper.vue
│ Expand/collapse UI (fixed height ↔ fullscreen)
│ Default height: 400px
▼
VisualizationFrame.vue
│ GET /api/plugins/{name} → plugin metadata
│ Create <iframe>
│ Inject <div id="app" data-incoming="{...}">
│ Load plugin script (entry_point.attr.src)
│ Load plugin CSS (entry_point.attr.css)
│ Listen for postMessage from iframe
▼
Plugin renders inside iframe
iframe Communication Protocol
The plugin receives data via data-incoming attribute on div#app:
{
"root": "https://galaxy.example.org/",
"visualization_config": {
"dataset_id": "...",
"settings": {...},
"tracks": [...]
},
"visualization_plugin": { /* full plugin metadata */ },
"visualization_title": "My Chart"
}
The plugin sends changes back via window.postMessage:
window.parent.postMessage({
from: "galaxy-visualization",
visualization_config: { /* updated config */ },
visualization_title: "Updated Title"
}, "*");
Changes are debounced (300ms) and emitted as change events up the component tree.
Backend: Markdown Processing of Visualization Blocks
Visualization blocks are handled differently from galaxy blocks:
-
Parsing:
visualizationis registered inVALID_ARGUMENTSwithDYNAMIC_ARGUMENTS— validation is completely bypassed. Any JSON content is accepted. -
Export:
handle_visualization()is a no-op in the lazy export handler — the block passes through unchanged for frontend rendering. -
PDF Export: Returns placeholder text
"*Visualization inputs not implemented*"— interactive visualizations cannot be rendered to static PDF. -
Invocation IDs: A dedicated regex (
VISUALIZATION_FENCED_BLOCK+INVOCATION_ID_JSON_PATTERN) finds and transforms"invocation_id": "..."values inside visualization JSON blocks during import/export. This is separate from the standard_remap_galaxy_markdown_callssystem.
# markdown_util.py
VISUALIZATION_FENCED_BLOCK = re.compile(
r"^```\s*visualization+\n\s*(.*?)^```", re.MULTILINE | re.DOTALL
)
INVOCATION_ID_JSON_PATTERN = re.compile(r'("invocation_id"\s*:\s*)"([^"]*)"')
def process_invocation_ids(f, workflow_markdown: str) -> str:
"""Find invocation_id values in visualization JSON blocks and apply f() to them."""
Used during:
ready_galaxy_markdown_for_import()— decode encoded IDsready_galaxy_markdown_for_export()— encode internal IDspopulate_invocation_markdown()— inject invocation.id
5. Plugin System Architecture
Plugin Discovery
Plugins are loaded from config/plugins/visualizations/ (and custom directories). The VisualizationsRegistry class discovers plugins via filesystem walk:
config/plugins/visualizations/
└── example/
└── static/
├── example.xml ← Config (name must match directory)
├── script.js ← Entry point
└── logo.svg ← Optional logo
A directory is a valid plugin if it contains static/{directory_name}.xml. Two-level nesting is supported.
Plugin XML Configuration
<?xml version="1.0" encoding="UTF-8"?>
<visualization name="Minimal Example" embeddable="true" hidden="false">
<description>A visualization plugin description</description>
<data_sources>
<data_source>
<model_class>HistoryDatasetAssociation</model_class>
<test test_attr="ext">tabular</test>
</data_source>
</data_sources>
<params>
<param required="true">dataset_id</param>
</params>
<entry_point entry_point_type="script" src="script.js" css="styles.css" />
<settings>
<input>
<name>chart_type</name>
<help>Select chart type</help>
<type>select</type>
</input>
</settings>
<tracks>
<input>
<name>data_column</name>
<help>Column to plot</help>
<type>data_column</type>
</input>
</tracks>
<specs>
<ai_prompt>You are a charting assistant...</ai_prompt>
</specs>
<help format="markdown"><![CDATA[
Plugin documentation in markdown format.
]]></help>
</visualization>
Key XML Attributes
| Element | Required | Purpose |
|---|---|---|
name (attr) | Yes | Display name |
embeddable (attr) | No | If true, appears in markdown editor toolbar |
hidden (attr) | No | If true, hidden from all UIs |
disabled (attr) | No | If present, plugin is skipped entirely |
data_sources | Yes | Which Galaxy object types are compatible |
model_class | Yes | HistoryDatasetAssociation, LibraryDatasetDatasetAssociation, or Visualization |
test | No | Filter by file attributes (e.g., ext=tabular) |
params | No | Required/optional parameters |
entry_point | Yes | JavaScript module or Mako template |
settings | No | Configuration inputs (axes, colors, etc.) |
tracks | No | Data series/track definitions |
specs | No | Custom config (e.g., ai_prompt for LLM-assisted charting) |
Plugins API
| Method | Path | Purpose |
|---|---|---|
GET | /api/plugins?embeddable=True | List embeddable plugins for editor toolbar |
GET | /api/plugins?dataset_id=X | List plugins compatible with a dataset |
GET | /api/plugins/{name} | Get plugin metadata + entry point |
GET | /api/plugins/{name}?history_id=X | List compatible datasets in a history |
POST | /api/plugins/{name}/chat/completions | LLM adapter using plugin’s ai_prompt spec |
Editor Integration
The markdown editor queries GET /api/plugins?embeddable=True and generates toolbar entries:
// services.ts
export async function getVisualizations(): Promise<Array<TemplateEntry>> {
const { data } = await axios.get(`${getAppRoot()}api/plugins?embeddable=True`);
return data.map((v: VisualizationType) => ({
title: v.html,
description: v.description || "",
logo: v.logo ? `${getAppRoot()}${v.logo}` : undefined,
cell: {
name: "visualization",
configure: true,
content: `{ "visualization_name": "${v.name}", "visualization_title": "${v.html}" }`,
},
}));
}
Clicking a toolbar entry inserts a ```visualization block with initial JSON config. The configure: true flag opens a configuration dialog.
Shipped Plugins
The Galaxy core repository ships only the example plugin (hidden by default). Real visualization plugins are distributed separately and installed into config/plugins/visualizations/. Known plugins from the Galaxy ecosystem include:
| Plugin | Description | Data Types |
|---|---|---|
| charts | Bar, line, scatter, pie charts | tabular, csv |
| trackster | Genome browser | BAM, BED, BigWig, VCF |
| graphviz | Network graphs (cytoscape.js) | graphml, json |
| chiraviz | Chimeric RNA visualization | tabular |
| csg | Chemical structure viewer | SDF, MOL2, PDB |
| jupyterlite | JupyterLite notebooks | various |
The Charts plugin (at charts.galaxyproject.org) provides the primary charting capability and is the most widely used visualization plugin.
6. Vitessce Visualizations (```vitessce blocks)
Spatial and single-cell data dashboards using the Vitessce framework.
Syntax
```vitessce
{
"version": "1.0.16",
"name": "Single-Cell Dashboard",
"datasets": [
{
"uid": "A",
"name": "My scRNA-seq",
"files": [
{
"__gx_dataset_id": "a1b2c3d4",
"fileType": "anndata.zarr"
}
]
}
],
"coordinationSpace": {...},
"layout": [...],
"__gx_height": 600
}
```
Special Galaxy Properties
Vitessce configs use __gx_ prefixed properties that are resolved before rendering:
| Property | Purpose |
|---|---|
__gx_dataset_id | Resolved to /api/datasets/{id}/display URL |
__gx_dataset_label | Resolved via invocation store (workflow context) |
__gx_dataset_name | Stripped (informational only) |
__gx_height | Extracted as visualization height, removed from config |
After resolution, the cleaned config is passed to the vitessce visualization plugin via VisualizationWrapper → VisualizationFrame.
7. Saved Visualization Model
Galaxy also has a Saved Visualization model (Visualization + VisualizationRevision) for persisting visualization configurations outside of markdown:
class Visualization(Base):
__tablename__ = "visualization"
id, user_id, title, type, dbkey, slug
deleted, importable, published
latest_revision_id → VisualizationRevision
revisions, tags, annotations, ratings, users_shared_with
class VisualizationRevision(Base):
__tablename__ = "visualization_revision"
id, visualization_id, title, dbkey
config # JSON blob with full visualization state
Saved visualizations can be shared, published, and annotated. They’re distinct from the inline markdown visualization blocks but use the same plugin infrastructure. A saved visualization’s type field corresponds to a plugin name.
8. Summary: When to Use What
| Need | Approach | Syntax |
|---|---|---|
| Quick inline chart with literal data | Vega-Lite | ```vega + JSON spec |
| Chart from a Galaxy dataset | Plugin visualization | ```visualization + JSON config |
| Dataset table/preview | Galaxy directive | ```galaxy + history_dataset_as_table(...) |
| Dataset as rendered image | Galaxy directive | ```galaxy + history_dataset_as_image(...) |
| Genome browser view | Plugin (trackster) | ```visualization + "visualization_name": "trackster" |
| Single-cell spatial dashboard | Vitessce | ```vitessce + JSON config |
| Inline dataset name/type in prose | Inline directive | ${galaxy history_dataset_name(...)} |
Key Design Principles
-
Vega-Lite is self-contained — the JSON spec includes everything needed to render. No Galaxy objects required. Best for ad-hoc charts with embedded or external data.
-
Plugin visualizations are data-bound — they reference Galaxy datasets by ID and render interactively in iframes. The plugin handles all rendering; Galaxy just provides the sandbox and data access.
-
Galaxy directives are server-resolved — the backend fetches actual dataset content, job metadata, etc. and either preserves the directive for frontend rendering (lazy) or expands to static HTML/markdown (eager/PDF).
-
Visualization blocks are frontend-only — the backend passes them through unchanged (except for invocation ID encoding). All resolution and rendering happens in Vue components.
-
Plugins are external — chart types, genome browsers, etc. are installable packages, not hardcoded. The
embeddableflag controls which ones appear in the markdown editor toolbar.