How to send dynamic charts with a Slack bot

Slack bots are limited by the Slack API to specific formats: text, images, and special controls. Critically, they cannot send Javascript or other dynamic formats. In order to send a chart or graph to your Slack channel, you must first render it as an image.

In this tutorial we'll use the free and open-source QuickChart API to generate chart images. To send these charts to Slack, you must:

  1. Construct the chart image
  2. Include it in your Slack bot's message

This tutorial is written assuming you are using Javascript, but the approach is general enough to work for any language.

Creating a chart image

If you're already showing these charts on a web frontend, your work here might already be done because QuickChart is built on top of Chart.js. You can just use your existing Chart.js config. Otherwise, we have to construct a Chart.js config object.

For example, imagine we have the following data:

Retweets Likes
12 80
5 42
40 215
5 30

First create an object using the Chart.js format:

const chart = {
type: 'bar',
data: {
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'],
datasets: [{
label: 'Retweets',
data: [12, 5, 40, 5]
}, {
label: 'Likes',
data: [80, 42, 215, 30]
}]
}
}

This object says that we have two data series, Retweets and Likes, and they each have values. The labels for the X axis are Weeks 1-4.

Next, we turn the JSON chart specification into a string:

const encodedChart = encodeURIComponent(JSON.stringify(chart));

And put it into our URL:

const chartUrl = `https://quickchart.io/chart?c=${encodedChart}`;

This gives a URL that encodes the chart object. Let's render that URL in an image tag:

<img src="https://quickchart.io/chart?c=..." />

We get the image below:

To learn more about static chart rendering:

Sending the chart image to Slack

Now that we've generated a chart image, let's send it to Slack.

You have a couple options here:

We'll go with the third choice because it is the most practical. We construct a block like so, using the image URL from above.

[
{
"type": "image",
"title": {
"type": "plain_text",
"text": "Latest data"
},
"block_id": "quickchart-image",
"image_url": "https://quickchart.io/chart?bkg=white&c=%7B%0A%20%20type%3A%20%27bar%27%2C%0A%20%20data%3A%20%7B%0A%20%20%20%20labels%3A%20%5B%27Week%201%27%2C%20%27Week%202%27%2C%20%27Week%203%27%2C%20%27Week%204%27%5D%2C%0A%20%20%20%20datasets%3A%20%5B%7B%0A%20%20%20%20%20%20label%3A%20%27Retweets%27%2C%0A%20%20%20%20%20%20data%3A%20%5B12%2C%205%2C%2040%2C%205%5D%0A%20%20%20%20%7D%2C%20%7B%0A%20%20%20%20%20%20label%3A%20%27Likes%27%2C%0A%20%20%20%20%20%20data%3A%20%5B80%2C%2042%2C%20215%2C%2030%5D%0A%20%20%20%20%7D%5D%0A%20%20%7D%0A%7D",
"alt_text": "Chart showing latest data"
}
]

You can try this directly in Slack's Block Kit Builder. Have a look at the Slack documentation and play around with the different settings:

In order to send the image blocks from your bot, use Slack's chat.postMessage API endpoint.

Starting with the basics, let's use this boilerplate for our communication with the Slack API. If you're using a helper library you probably already have a way to send messages, but here is the boilerplate I always use:

const request = require('request');

// Put your bot token here.
const SLACK_BOT_USER_TOKEN = 'xyz123';

function sendMessage(data) {
request({
url: 'https://slack.com/api/chat.postMessage',
method: 'POST',
json: data,
headers: {
'Content-Type': 'application/json; charset=utf-8',
Authorization: `Bearer ${SLACK_BOT_USER_TOKEN}`,
},
},
function(error, response, body) {
if (error || response.statusCode !== 200) {
console.error('Error sending slack response:', error);
} else if (!response.body.ok) {
console.error('Slack responded with error:', response.body);
} else {
// All good!
}
},
);
}

Now, we'll put it all together and send the message with chart image URL:

sendMessage({
text: 'Chart data update',
blocks: [
{
"type": "image",
"title": {
"type": "plain_text",
"text": "Latest data"
},
"block_id": "quickchart-image",
"image_url": "https://quickchart.io/chart?bkg=white&c=%7B%0A%20%20type%3A%20%27bar%27%2C%0A%20%20data%3A%20%7B%0A%20%20%20%20labels%3A%20%5B%27Week%201%27%2C%20%27Week%202%27%2C%20%27Week%203%27%2C%20%27Week%204%27%5D%2C%0A%20%20%20%20datasets%3A%20%5B%7B%0A%20%20%20%20%20%20label%3A%20%27Retweets%27%2C%0A%20%20%20%20%20%20data%3A%20%5B12%2C%205%2C%2040%2C%205%5D%0A%20%20%20%20%7D%2C%20%7B%0A%20%20%20%20%20%20label%3A%20%27Likes%27%2C%0A%20%20%20%20%20%20data%3A%20%5B80%2C%2042%2C%20215%2C%2030%5D%0A%20%20%20%20%7D%5D%0A%20%20%7D%0A%7D",
"alt_text": "Chart showing latest data"
}
]
});

That's it! Our message is sent and the graph appears in Slack.