ᚱᛗ
© 2022
Powered by Hugo

Plotly 2.0 in Hugo

Table of Contents

Summary

In this post I share my workflow to integrate Plotly 2.0 with Hugo. If you google “Plotly in Hugo” you will most likely find a few blog posts dealing with Plotly v1.58.512. Even though that version still works, Plotly’s current version is v2.11.1 (at the time of this writing), and it requires a somewhat different setup. The latest version is more optimized and allows for newer features (e.g., annotated heatmaps), so I recommend upgrading. I go over the steps to produce plots in Plotly.py, and integrate them in Hugo using Plotly.js v2.

Tested with: plotly.py: v5.6.0; plotly.js: v2.11.1; hugo: v0.88.1

A Note on Plotly’s Versioning

As explained by Plotly’s team in this announcement , when the team released Plotly 2.0 there were breaking changes. One of the most important for our purposes was the deprecation of Plotly.d3.json3. To avoid breaking applications the Plotly team decided to pin https://cdn.plot.ly/plotly-latest.min.js to v1.58.5. So keep in mind that latest doesn’t actually point to the latest version—instead, the team recommends the user explicitly manage the version of the library that they are using.

Demo Workflow

Figure 1. Mt Bruno Elevation

Example taken from Plotly’s 3D Surface Plots

1. Create the Plot in Python & Save it as json

import plotly.graph_objects as go
import pandas as pd

z_data = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv"
)
fig = go.Figure(
    data=go.Surface(z=z_data.values),
    layout=go.Layout(
        title="Mt Bruno Elevation",
        width=500,
        height=500,
    ),
)
fig.update_layout(template="plotly_dark", title="Mt Bruno Elevation")
fig.write_json("mtbruno.json")

Once you have created your figure in Python, you need to convert it to json. Then you have to place it somewhere inside the my-hugo/static folder, personally I have all my json plots in my-hugo/static/json. You can hardcode the target path to avoid the manual copy-paste step; this is useful if you’re going to be iterating over many plots.

2. Conditionally Load d3.js and plotly.js

Go to my-hugo/layouts/partials/head.html and add the following lines (one-off):

<!-- Embed d3.js and plotly.js if plotly:true -->
{{ if .Params.plotly }}
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdn.plot.ly/plotly-2.11.1.min.js"></script>
{{ end }}

3. Create the Plotly Shortcode

Go to my-hugo/layouts/shortcodes and create a file named plotly.html, then write this in it (one-off):

{{ $json := .Get "json" }}
{{ $height := .Get "height" | default "200px" }}
<div id="{{$json}}" class="plotly" style="height:{{$height}};"></div>
<script>
  d3.json({{ $json }}).then(function (fig) {
      Plotly.newPlot('{{$json}}', fig.data, fig.layout, { responsive: true });
  });
</script>

4. In Post.md: Set plotly: true and Call the Shortcode

If a post contains a Plotly plot, then add plotly: true to your post config metadata. This line is checked in Step 2 by {{ if .Params.plotly }}, thus triggering the download of d3.js and plotly.js only if actually required, on a post-by-post basis.

Finally, call your shortcode on the json file and adjust the height if needed:

{{< plotly json="/json/mtbruno.json" height="500px" >}}

Notes


  1. Plotly & Hugo  ↩︎

  2. Including Plotly Figures in Hugo Posts  ↩︎

  3. See the full changelog  ↩︎