Building contextual search experiences using Block Kit and Node.js

By Tomomi Imura

Published: May 14, 2019
Updated: January 31, 2022

title masthead

We've just released Block Kit, a UI framework that enables you to build rich and interactive experiences in Slack. This gives your apps more options to display information in each message. As an example, you can list information like tasks or poll results.

With Block Kit, you can prototype with a visual tool, the Block Kit Builder, as well as actually build messages sent by your app using the JSON string generated by the tool.

This tutorial walks you through building messages with Block Kit to improve the user experience of search results in your app.


This instruction uses Node.js to show you how to build a slash command that displays results in a rich messaging format. Basic coding experience with JavaScript or other programming languages is recommended to complete this tutorial.

The source code you'll need is on Glitch, which you can "remix" and run without deploying code!

🎏🥫 Source code on Glitch

🎏🍴 Remix (fork) the Glitch repo

Also, to make the example app more interesting, we'll use the Yelp API, which requires you to sign up to obtain a Yelp Client ID and an API key. Detailed information on Yelp's API can be found here.

Let's get started with a walkthrough of how a user might interact with this app:

  • A user sends a slash command, /find-food [a type of food], [location proximity] (e.g. /find-food burrito, Mission San Francisco)
  • Your app responds to the command and shows three nearby restaurant results.

Responding to a Slash Command

In this exercise, we are going to create a slash command where a user can send the /find-food command to get a list of a few restaurants as the result.

Setting Up an App

First, go to Slack App Management to create an app by clicking the button:

Create a Slack app

Then add the slash command feature and define your command at Features > Slash Commands at the App Management page. For this tutorial, let's call it /find-food. The parameters a user can pass with the command will be a type of food and a location, separated by a comma, as in /find-food bao, Chinatown NYC.

This will automatically create a bot user and add the commands bot scope for you.

Your Request URL should be https://your-server/route. If you "remix" the project on Glitch, where you will have a generated project name consisting of two random words, your request URL would be something like


Then, get your Signing Secret key at Settings > Basic information.

The key should be stored in the .env file, along with Yelp credentials.


(If you're trying on the remixed Glitch example, rename the .env.sample in the repo to .env, and fill it out with your credentials!)

Go to Settings > Install App to install the app on your workspace.

In your Node.js code, include dependencies and run an Express server, also get the raw request payload to verify your Signing Secret. (For more details about this security feature, please refer to the Verifying the Requests section in the previous tutorial):

const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const signature = require('./verifySignature');

const app = express();

const rawBodyBuffer = (req, res, buf, encoding) => {
  if (buf && buf.length)  req.rawBody = buf.toString(encoding || 'utf8');

app.use(bodyParser.urlencoded({verify: rawBodyBuffer, extended: true }));
app.use(bodyParser.json({ verify: rawBodyBuffer }));

const server = app.listen(process.env.PORT || 5000);

Sending a Simple Message

Next, use an HTTP POST method route to create an endpoint to receive the slash command payload. Once you receive a payload, check if the request is coming from Slack (otherwise, you may be under attacks from some malicious source!). If it is a valid and safe request, parse the user-send params to get queries, in this case, food type, and location.

Then, pass the queries to Yelp API to get the results.'/command', async (req, res) => {

  if(!signature.isVerified(req)) {
  } else {
    const query = req.body.text ? req.body.text : 'lunch, San Francisco';
    const queries = query.split(',');
    const term = queries.shift(); // "Pizza" 
    const location = queries; // "San Francisco, CA"
    const places = await getPlaces(query, location);
  const message = { // will generate it }

The getPlaces() returns the results in an array. (See the source code for the function.) The returned array from the Yelp REST API would look like this (Note: the result shown here is simplified for this tutorial):

 name: 'Zero Zero',
 review_count: 3128,
 rating: 4,
 price: '$$',
    display_address: [ '826 Folsom St', 'San Francisco, CA 94107' ] },
    phone: '+14153488800'

Replying the user with a simple message publicly, you just send an HTTP 200 with a simple JSON. For example, to simply respond with a restaurant name from the result array:

const message = {
    response_type: 'in_channel',
    text: places[0].name,

This JSON response will display a message like this:

Simple message

Block-Kit-ify Your Message

Now, let's take advantage of the new Block Kit elements to send a more contextual message.

Block Kit consists of stackable blocks that you can mix and match layout blocks and block elements to build a message.

We are going to re-format the JSON message we just created to display a plain text into this rich message format using Block Kit:

Block Kit message anatomy GIF

You can design a message using a visual prototyping sandbox, Block Kit Builder, or by picking and arranging all available blocks on a browser.

If you click each block from the left side, the JSON array is generated for you on the right side to use in your code:

Block Kit Builder GIF

Let's bring the JSON array into your message object to replace the previous one. Replace the line where it says text: places[0].name with the array of blocks:

const message = {
  response_type: 'in_channel',   
  blocks: [
    // Result 1
      type: 'section',
      text: {
        type: 'mrkdwn',
        text: `*<${places[0].url}|${places[0].name}>* \n${places[0].location.display_address.join(' ')} \n\nRating: ${places[0].rating} \nPrice: ${places[0].price}`
      accessory: {
          type: 'image',
          image_url: `${places[0].image_url}`,
          alt_text: 'venue image'
        'type': 'context',
        'elements': [
            'type': 'plain_text',
            'text': `${places[0].review_count} Yelp reviews`,
            'emoji': true
        'type': 'divider'

When you set the text type as mrkdwn, you can format your text using some of the markdown features, such as bold and italic text, and hyper-linked text, etc. You can read more about text formatting at An overview of message composition.

Now try the slash command on Slack client, and tada, now you have a nicer and more contextual message as the response!

more contexual message with Block Kit

Ta-da! Now the Yelp results are displayed nicely!

How Else Can You Send Rich Messages?

Now that you've created a rich message as a response to a slash command, you can send messages with a variety of methods such as incoming webhooks, as well as in-app via WebAPI with either chat.postMessage and chat.postEphemeral, including the responses for message actions and rich interactions.

Best Practices

We encourage you to build creative and imaginative message blocks with the Block Kit Builder, however, you must always consider your users first. Above all else, your messages must be easy to read. Avoid cluttering up the conversation and do not to load with a lot of content into a single message. Also, use interactive UI elements like buttons and menus effectively.

First, prototype a message UI using the Block Kit Builder, try multiple patterns and test with mobile devices too.

I hope this tutorial helped you to get some ideas on what to build, or modify your existing Slack app!

If you run into any trouble, find us on Twitter, or reach out our developer support team at Happy developing! 🤖

Related documentation

Was this page helpful?