The dash-down package provides tools to render markdown files into Plotly Dash component trees. Besides standard markdown syntax, a custom interpretation of the directive syntax extension makes it possible to embed Dash code blocks and/or applications (including callbacks). For a live demo, please take look at the dash-extensions documentation.
Make sure that you have setup uv. Then run
uv venv && source .venv/bin/activate && uv sync --all-extras --dev
to create a new virtual environment with all dependencies installed.
uv run python example.py
uv run python example_inline.py
uv run python example_custom_directive.py
uv run python example_custom_code_renderer.py
uv run pytest
In addition to rendering markdown from files, you can also render inline markdown,
md_to_blueprint_html(md="# This is a heading").embed(app)
A complete example can be found in example_inline.py.
Custom content is rendered via the markdown directive syntax extension. A directive has the following syntax,
.. directive-name:: directive value
:option-key: option value
:option-key: option value
full featured markdown text here
where the directive-name is mandatory, while the value, the options (specified as key value pairs), and the text are optional.
Currently, the bundled directives are
- api-doc - a directive for rendering api documentation for a component
- dash-proxy - a directive for rendering dash apps (including interactivity)
The easiest way to create a custom directive is to create a function with the following signature,
from dash_extensions.enrich import DashBlueprint
def directive_name(value: str, text: str, options: dict[str, str], blueprint: DashBlueprint):
"""
:param value: the directive value (optional)
:param text: the markdown text (optional)
:param options: a dict containing all key value pairs (optional)
:param blueprint: the DashBlueprint of the resulting Dash component tree, used e.g. for callback registration
:return: a Dash component
"""
...Say, we want to make a new directive that yields a plot of the iris dataset. The code would then be along the lines of,
import plotly.express as px
from dash_extensions.enrich import dcc, DashBlueprint
def graph(value: str, text: str, options: dict[str, str], blueprint: DashBlueprint):
df = getattr(px.data, options.dataset)()
fig = px.scatter(df, x=options.x, y=options.y)
return dcc.Graph(figure=fig)With this directive defined, it is now possible to create a graph similar to the one in the Dash docs with the following syntax,
.. graph::
:dataset: iris
:x: sepal_width
:y: sepal_length
To render a markdown file using the new, shiny directive, the syntax would be,
from dash_extensions.enrich import DashProxy
from dash_down.express import md_to_blueprint_dmc
...
blueprint = md_to_blueprint_html('path_to_your_md_file', directives=[graph])
app = DashProxy(blueprint=blueprint)
if __name__ == '__main__':
app.run()A working example is bundled in the repo (see example_custom_directive.py).
The layout of the blueprint returned by the renderer can be customized by passing a custom app shell via the shell keyword of the md_to_blueprint_html function. A working example is bundled in the repo (see example_code_renderer.py).
The layout of the Dash apps rendered via the DashProxyDirective can be customized via the dash_proxy_shell keyword of the md_to_blueprint_html function. A working example is bundled in the repo (see example_code_renderer.py).
Per default, the app shell Div element with the code rendered as the first child and the resulting app rendered as the second.
Make a subclass of DashMantineRenderer (or DashHtmlRenderer, if you prefer to start from raw HTML) and override the render function(s) for any element that you want to change. A good place to start would be to look at the DashMantineRenderer class itself for inspiration.
The bundled renderers ( DashHtmlRenderer, DashMantineRenderer) add CSS classes to the rendered elements with the following naming structure,
m2d-{DIRECTIVE NAME}
Hence, you can target all paragraphs via the m2d-paragraph class, or all bold text via the m2d-strong class.