Skip to main content

Writing New Templates

MultiQC is built around a templating system that uses the Jinja python package. This makes it very easy to create new report templates that fit your needs.

Core or plugin

If your template could be of use to others, it would be great if you could add it to the main MultiQC package. You can do this by creating a fork of the MultiQC GitHub repository, adding your template and then creating a pull request to merge your changes back to the main repository.

If it's very specific template, you can create a new Python package which acts as a plugin. For more information about this, see the plugins documentation.

Creating a template skeleton

For a new template to be recognised by MultiQC, it must be a python submodule directory with a __init__.py file. This must be referenced in the setup.py installation script as an entry point.

You can see the bundled templates defined in this way:

entry_points = {
'multiqc.templates.v1': [
'default = multiqc.templates.default',
'simple = multiqc.templates.simple',
'geo = multiqc.templates.geo',
]
}

Note that these entry points can point to any Python modules, so if you're writing a plugin module you can specify your module name instead. Just make sure that multiqc.templates.v1 is the same.

Once you've added the entry point, remember to install the package again:

pip install -e .

Using -e tells pip to softlink the plugin files instead of copying, so changes made whilst editing files will be reflected when you run MultiQC.

The __init__.py files must define two variables - the path to the template directory and the main jinja template file:

template_dir = os.path.dirname(__file__)
base_fn = 'base.html'

Child templates

The default MultiQC template contains a lot of code. Importantly, it includes 1448 lines of custom JavaScript (at time of writing) which powers the plotting and dynamic functions in the report. You probably don't want to rewrite all of this for your template, so to make your life easier you can create a child template.

To do this, add an extra variable to your template's __init__.py:

template_parent = 'default'

This tells MultiQC to use the template files from the default template unless a file with the same name is found in your child template. For instance, if you just want to add your own logo in the header of the reports, you can create your own header.html which will overwrite the default header.

Files within the default template have comments at the top explaining what part of the report they generate.

Child templates can also inherit template functions from their parent. For example, the default template provides the material_icon function which can be used in any child template without additional configuration.

Extra init variables

There are a few extra variables that can be added to the __init__.py file to change how the report is generated.

Setting output_dir instructs MultiQC to put the report and it's contents into a subdirectory. Set the string to your desired name. Note that this will be prefixed if -p/--prefix is set at run time.

Secondly, you can copy additional files with your report when it is generated. This is usually used to copy required images or scripts with the report. These should be a list of file or directory paths, relative to the __init__.py file. Directory contents will be copied recursively.

You can also override config options in the template. For example, setting the value of config.plots_force_flat can force the report to only have static image plots.

from multiqc.utils import config

output_subdir = 'multiqc_report'
copy_files = ['assets']
config.plots_force_flat = True

Development mode

When developing a template, you can use the development: true config option or the --development command line flag. This instructs MultiQC not to embed source files directly into the HTML and instead link to the MultiQC source code files:

  • JavaScript and CSS files are loaded directly from the source code template directory instead of being embedded
  • Plot images are linked from external files rather than being embedded as base64 data URIs
  • Plot data is exported as an uncompressed JSON file (multiqc_plots.js) in the data directory

This allows you to see changes to your template files immediately without rebuilding or recompiling. Simply refresh the report in your browser after making changes.

Jinja template variables

There are a number of variables that you can use within your Jinja template. Two namespaces are available - report and config. You can print these using the Jinja curly brace syntax, eg. {{ config.version }}. See the Jinja2 documentation for more information.

The default MultiQC template includes dependencies in the HTML so that the report is standalone. If you would like to do the same, use the include_file function. For example:

<script>
{
{
include_file("js/jquery.min.js");
}
}
</script>
<img src="data:image/png;base64,{{ include_file('img/logo.png', b64=True) }}" />

Material Design Icons

The default template includes a material_icon function that embeds Material Design Icons as inline SVG. This function is available to child templates that inherit from the default template. Usage:

{{ material_icon('delete') }}
{{ material_icon('warning', 16) }}
{{ material_icon('info', 20, '#0066cc') }}

The function takes three parameters:

  • icon_name (required): Name of the Material Design Icon (e.g., 'delete', 'info', 'warning')
  • size (optional, default 24): Size of the icon in pixels
  • color (optional, default 'currentColor'): Color of the icon

The function will try to load the filled variant first, then fall back to the outlined variant if the filled version is not found. If the icon cannot be found, it returns an empty string. In strict mode (--strict), missing icons will be reported as errors.

Appendices

Custom plotting functions

If you don't like the default plotting functions built into MultiQC, you can write your own! If you create a callable variable in a template called either bargraph or linegraph, MultiQC will use that instead. For example:

def custom_linegraph(plotdata, pconfig):
return '<h1>Awesome line graph here</h1>'
linegraph = custom_linegraph

def custom_bargraph(plotdata, plotseries, pconfig):
return '<h1>Awesome bar graph here</h1>'
bargraph = custom_bargraph

These particular examples don't do very much, but hopefully you get the idea. Note that you have to set the variable linegraph or bargraph to your function.