Skip to content
Build JSON object for Slack Block Kit from readable JSX
Branch: master
Clone or download
Latest commit e77e1f9 Mar 13, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci Revert ignored audit Mar 5, 2019
demo
docs
src Add BlockProps alias type to BlocksProps Mar 13, 2019
test
.eslintignore [WIP] Add dry demo page Feb 26, 2019
.eslintrc.yml Disallow children in Image block component Feb 19, 2019
.gitignore [WIP] Add dry demo page Feb 26, 2019
.nvmrc
.prettierignore Update format and style Feb 27, 2019
CHANGELOG.md
LICENSE Update LICENSE Feb 26, 2019
README.md Update README.md to use Blocks instead of Block Mar 13, 2019
jest.config.js Require Node.js >= 8 Feb 26, 2019
package.json
tsconfig.json Require Node.js >= 8 Feb 26, 2019
version.js Add versioning script for bumping Feb 26, 2019
yarn.lock

README.md

jsx-slack

CircleCI npm LICENSE

Build JSON object for Slack block kit from readable JSX.


👉 Try our REPL demo in https://speee-jsx-slack.netlify.com/.

Features

Motivation

When developing Slack-integrated app, continuous maintenance of the rich contents is a difficult task. A team member must read and write JSON with deep knowledge about a specification of Slack messaging.

Slack has shipped Block Kit and Block Kit Builder, and efforts to develop app easily. We believe JSX-based template would enhance a developer experience of Slack app to the next stage.

Project goal

A project goal is creating an interface to build a maintainable Slack message with confidence via readable JSX.

jsx-slack would allow building message blocks with predictable HTML-like markup. It helps in understanding the structure of the complex message.

Install

We require Node.js >= 8. If you are using TypeScript, we also require TS >= 3.0.

# npm
npm install --save @speee-js/jsx-slack
# yarn
yarn add @speee-js/jsx-slack

Block Kit as component

Slack has recommended to use Block Kit for building tempting message. By using jsx-slack, you can build a template with piling up Block Kit blocks by JSX. It is feeling like using components in React or Vue.

Usage

JSX Transpiler

When you want to use jsx-slack with JSX transpiler (Babel / TypeScript), you have to setting to use imported our parser JSXSlack.h. Typically, we recommend to use pragma comment /* @jsx JSXSlack.h */.

This is a simple block example example.jsx just to say hello to someone. Wrap JSX by JSXSlack() function.

/** @jsx JSXSlack.h */
import JSXSlack, { Blocks, Section } from '@speee-js/jsx-slack'

export default function exampleBlock({ name }) {
  return JSXSlack(
    <Blocks>
      <Section>
        Hello, <b>{name}</b>!
      </Section>
    </Blocks>
  )
}

A prgama would work in Babel (@babel/plugin-transform-react-jsx) and TypeScript with --jsx react. You can use jsx-slack in either one.

Template literal

A much simpler way to build blocks is using jsxslack tagged template literal.

It allows the template syntax almost same as JSX, powered by htm (Hyperscript Tagged Markup). The troublesome transpiler setup and importing built-in components are not required.

import { jsxslack } from '@speee-js/jsx-slack'

export default function exampleBlock({ name }) {
  return jsxslack`
    <Blocks>
      <Section>
        Hello, <b>${name}</b>!
      </Section>
    </Blocks>
  `
}

Use template in Slack API

After than, just use created template in Slack API. We are using the official Node SDK @slack/client in this example. See also Slack guide.

import { WebClient } from '@slack/client'
import exampleBlock from './example'

const web = new WebClient(process.env.SLACK_TOKEN)

web.chat
  .postMessage({
    channel: 'C1232456',
    blocks: exampleBlock({ name: 'Yuki Hattori' }),
  })
  .then(res => console.log('Message sent: ', res.ts))
  .catch(console.error)

It would post a simple Slack message like this:

JSX component

Blocks

<Blocks>

A container component to use Block Kit. You should wrap Block Kit elements by <Blocks>.

⚠️ A confusable <Block> component has deprecated in v0.4.1. See also #11.

<Section>: Section Block

Display a simple text message. You have to specify the content as children. It allows formatting with HTML-like elements.

<section> intrinsic HTML element works as well.

<Blocks>
  <Section>Hello, world!</Section>
</Blocks>

Props
  • id / blockId (optional): A string of unique identifier of block.
Accessory

The content of <Section> may include one of an accessory component. A defined element will show in side-by-side of text.

<Blocks>
  <Section>
    You can add an image next to text in this block. :point_right:
    <Image src="https://placekitten.com/256/256" alt="Accessory image" />
  </Section>
</Blocks>

Accessory components
<Field>: Fields for section block

In addition the text content, the section block also can use 2 columns texts called fields. In jsx-slack, you can define field by <Field> component in <Section> block.

<Blocks>
  <Section>
    About this repository:
    <Field>
      <b>Name</b>
      <br />
      speee/jsx-slack
    </Field>
    <Field>
      <b>Maintainer</b>
      <br />
      Yuki Hattori
    </Field>
    <Field>
      <b>Organization</b>
      <br />
      Speee, Inc.
    </Field>
    <Image src="https://github.com/speee.png" alt="Speee, Inc." />
  </Section>
</Blocks>

Contents of <Field> would be placed after the main text contents even if placed them anywhere.

<Divider>: Divider Block

Just a divider. <hr> intrinsic HTML element works as well.

<Blocks>
  <Divider />
</Blocks>

Props
  • id / blockId (optional): A string of unique identifier of block.

<Image>: Image Block

Display an image block. It has well-known props like <img> HTML element.

In <Blocks>, <img> intrinsic HTML element works as well.

<Blocks>
  <Image src="https://placekitten.com/500/500" alt="So cute kitten." />
</Blocks>

Props
  • src (required): The URL of the image.
  • alt (required): A plain-text summary of the image.
  • title (optional): An optional title for the image.
  • id / blockId (optional): A string of unique identifier of block.

<Actions>: Actions Block

A block to hold interactive elements. Slack allows a maximum of 25 interactive elements in <Actions> (But recommends to place up to 5 elements).

Props
  • id / blockId (optional): A string of unique identifier of block.

<Context>: Context Block

Display message context. It allows mixed contents consisted of the text and the <img> tag image.

<Blocks>
  <Context>
    <img src="https://placekitten.com/100/100" alt="Kitten" />
    A kitten and
    <img src="https://placekitten.com/100/100" alt="Kitten" />
    more kitten.
  </Context>
</Blocks>

⚠️ Slack restricts the number of elements consisted of text content and image up to 10. jsx-slack throws error if the number of generated elements is going over the limit.

Props
  • id / blockId (optional): A string of unique identifier of block.

Interactive elements

Some blocks may include the interactive component to exchange info with Slack app.

<Button>: Button element

A simple button to send action to registered Slack App, or open external URL.

<Blocks>
  <Actions>
    <Button actionId="action" value="value">
      Action button
    </Button>
    <Button url="https://example.com/">Link to URL</Button>
  </Actions>
</Blocks>

Props
  • actionId (optional): An identifier for the action.
  • value (optional): A string value to send to Slack App when clicked button.
  • url (optional): URL to load when clicked button.
  • confirm (optional): <Confirm> element to show confirmation dialog.

<Select>: Select menu with static options

A menu element with static options passed by <Option> or <Optgroup>. It has a interface similar to <select> HTML element.

<Blocks>
  <Actions>
    <Select actionId="rating" placeholder="Rate it!">
      <Option value="5">5 :star::star::star::star::star:</Option>
      <Option value="4">4 :star::star::star::star:</Option>
      <Option value="3">3 :star::star::star:</Option>
      <Option value="2">2 :star::star:</Option>
      <Option value="1">1 :star:</Option>
    </Select>
  </Actions>
</Blocks>

Props
  • actionId (optional): An identifier for the action.
  • placeholder (optional): A plain text to be shown at first.
  • value (optional): A value of item to show initially. It must choose value from defined <Option> elements in children.
  • confirm (optional): <Confirm> element to show confirmation dialog.
<Option>: Menu item
Props
  • value (required): A string value to send to Slack App when choose item.
<Optgroup>: Group of menu items
<Blocks>
  <Actions>
    <Select actionId="action" placeholder="Action...">
      <Optgroup label="Search with">
        <Option value="search_google">Google</Option>
        <Option value="search_bing">Bing</Option>
        <Option value="search_duckduckgo">DuckDuckGo</Option>
      </Optgroup>
      <Optgroup label="Share to">
        <Option value="share_facebook">Facebook</Option>
        <Option value="share_twitter">Twitter</Option>
      </Optgroup>
    </Select>
  </Actions>
</Blocks>

Props
  • label (required): A plain text to be shown as a group name.

<ExternalSelect>: Select menu with external data source

You should use <ExternalSelect> if you want to provide the dynamic list from external source.

It requires setup JSON entry URL in your Slack app. Learn about external source in Slack documentation.

<Blocks>
  <Actions>
    <ExternalSelect
      actionId="category"
      placeholder="Select category..."
      minQueryLength={2}
    />
  </Actions>
</Blocks>

Props
  • actionId (optional): An identifier for the action.
  • placeholder (optional): A plain text to be shown at first.
  • initialOption (optional): An initial option exactly matched to provided options from external source. It allows raw JSON object or <Option>.
  • minQueryLength (optional): A length of typed characters to begin JSON request.
  • confirm (optional): <Confirm> element to show confirmation dialog.
<SelectFragment>: Generate options for external source

You would want to build not only the message but also the data source by jsx-slack. <SelectFragment> component can create JSON object for external data source usable in <ExternalSelect>.

A following is a simple example to serve JSON for external select via express. It is using jsxslack tagged template literal.

import { jsxslack } from '@speee-js/jsx-slack'
import express from 'express'

const app = express()

app.get('/external-data-source', (req, res) => {
  res.json(jsxslack`
    <SelectFragment>
      <Option value="item-a">Item A</Option>
      <Option value="item-b">Item B</Option>
      <Option value="item-c">Item C</Option>
    </SelectFragment>
  `)
})

app.listen(80)

<UsersSelect>: Select menu with user list

A select menu with options consisted of users in the current workspace.

Props
  • actionId (optional): An identifier for the action.
  • placeholder (optional): A plain text to be shown at first.
  • initialUser (optional): The initial user ID.
  • confirm (optional): <Confirm> element to show confirmation dialog.

<ConversationsSelect>: Select menu with conversations list

A select menu with options consisted of any type of conversations in the current workspace.

Props
  • actionId (optional): An identifier for the action.
  • placeholder (optional): A plain text to be shown at first.
  • initialConversation (optional): The initial conversation ID.
  • confirm (optional): <Confirm> element to show confirmation dialog.

<ChannelsSelect>: Select menu with channel list

A select menu with options consisted of public channels in the current workspace.

Props
  • actionId (optional): An identifier for the action.
  • placeholder (optional): A plain text to be shown at first.
  • initialChannel (optional): The initial channel ID.
  • confirm (optional): <Confirm> element to show confirmation dialog.

<Overflow>: Overflow menu

An overflow menu displayed as ... can access to some hidden menu items by many actions. It must contain least of 2 <OverflowItem> components.

<Blocks>
  <Actions>
    <Overflow actionId="overflow_menu">
      <OverflowItem value="share">Share</OverflowItem>
      <OverflowItem value="reply">Reply message</OverflowItem>
      <OverflowItem url="https://example.com/">Open in browser</OverflowItem>
    </Overflow>
  </Actions>
</Blocks>

Props
  • actionId (optional): An identifier for the action.
  • confirm (optional): <Confirm> element to show confirmation dialog when clicked menu item.
<OverflowItem>: Menu item in overflow menu
Props
  • value (optional): A string value to send to Slack App when choose item.
  • url (optional): URL to load when clicked button.

<DatePicker>: Select date from calendar

An easy way to let the user selecting any date is using <DatePicker> component.

<Blocks>
  <Actions>
    <DatePicker actionId="date_picker" initialDate={new Date()} />
  </Actions>
</Blocks>

Props
  • actionId (optional): An identifier for the action.
  • placeholder (optional): A plain text to be shown at first.
  • initialDate (optional): An initially selected date. It allows YYYY-MM-DD formatted string, UNIX timestamp in millisecond, and JavaScript Date instance.
  • confirm (optional): <Confirm> element to show confirmation dialog.

Components for composition objects

<Confirm>: Confirmation dialog

Define confirmation dialog. Some interactive elements can open confirmation dialog when selected, by passing <Confirm> to confirm prop.

<Blocks>
  <Actions>
    <Button
      actionId="commit"
      value="value"
      confirm={
        <Confirm title="Commit your action" confirm="Yes, please" deny="Cancel">
          <b>Are you sure?</b> Please confirm your action again.
        </Confirm>
      }
    >
      Commit
    </Button>
  </Actions>
</Blocks>

Props
  • title (required): The title of confirmation dialog.
  • confirm (required): A text content of the button to confirm.
  • deny (required): A text content of the button to cancel.

HTML-like formatting

Slack can format message by very rational short syntaxes called "mrkdwn". On the other hand, someone might yearn for a template engine with clear tag definition like HTML, especially when building a complex message.

jsx-slack has HTML-compatible JSX elements to format messages. It might be verbose as a text, but would give readablity by well-known HTML elements.

You may also use a regular mrkdwn syntax to format if necessary.

Format text style

  • <i>, <em>: Italic text
  • <b>, <strong>: Bold text
  • <s>, <strike>, <del>: Strikethrough text
  • <code>: Inline code

Line breaks

As same as HTML, line breaks in JSX will be ignored, and replace to a single whitespace. You shoud use <br /> tag in this case.

HTML block contents

  • <p> tag just makes a blank line around contents. Slack would render it as like as paragraph.
  • <blockquote> adds > character to the first of each lines for highlighting as quote.
  • <pre> tag will recognize the content as formatted-text, and wrapped content by ``` .

List simulation

We can simulate the list provided from <ul> and <ol> tag by using mimicked text.

<ul>
  <li>Item A</li>
  <li>
    Item B
    <ul>
      <li>Sub item 1</li>
      <li>
        Sub item 2
        <ul>
          <li>and more...</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    Item C
    <ol>
      <li>Ordered item 1</li>
      <li>Ordered item 2</li>
    </ol>
  </li>
</ul>

The above would be replaced to just a plain text like this:

• Item A
• Item B
  ◦ Sub item 1
  ◦ Sub item 2
    ▪︎ and more...
• Item C
  1. Ordered item 1
  2. Ordered item 2

Links

jsx-slack will not recognize URL-like string as hyperlink. You should use <a href=""> if you want using a link.

For example, <a href="https://example.com/">Link</a> will be converted to <https://example.com/|Link>, and rendered as like as "Link".

To Slack channel

<a href="#C024BE7LR" /> means a link to Slack channel. You have to set PUBLIC channel's ID, not channel name, as an anchor. Refer details to documentation by Slack for more details.

If defined what except URL as href attribute, you cannot use a custom content because Slack would fill the content automatically. Unlike HTML specification, <a> tag allows to use as void element <a />.

Mention to user and user group

As like as channel link, <a href="@U024BE7LH" /> means a mention to specified user.

jsx-slack can mention to user groups with a same syntax <a href="@SAZ94GDB8" /> by detecting user group ID prefixed S.

Of course, we also support special mentions like @here, @channel, and @everyone.

Date formatting

Slack supports date formatting for localization by timezone. jsx-slack also support it by HTML5 <time> tag.

<time datetime="1392734382">{'Posted {date_num} {time_secs}'}</time>
// => "<!date^1392734382^Posted {date_num} {time_secs}|Posted 2014-02-18 14:39:42 PM>"

<time datetime="1392734382">{'{date} at {time}'}</time>
// => "<!date^1392734382^{date} at {time}|February 18th, 2014 at 14:39 PM>"

<a href="https://example.com/">
  <time datetime="1392734382" fallback="Feb 18, 2014 PST">
    {'{date_short}'}
  </time>
</a>
// => "<!date^1392734382^{date_short}^https://example.com/|Feb 18, 2014 PST>"

An optional fallback text may specify via additional fallback attribute. If it is not defined, jsx-slack will generate the fallback text in UTC from template string.

Correspondence table

Basics

jsx-slack Slack mrkdwn
<i>Italic</i> _Italic_
<em>Italic</em> _Italic_
<b>Bold</b> *Bold*
<strong>Bold</strong> *Bold*
<s>Strike</s> ~Strike~
<del>Strike</del> ~Strike~
Line<br />break Line\nbreak
<p>foo</p><p>bar</p> foo\n\nbar
<blockquote>quote</blockquote> &gt; quote
<code>code</code> `code`
<pre>{'code\nblock'}</pre> ```\ncode\nblock\n```
<ul><li>List</li></ul> • List

Links

jsx-slack Slack mrkdwn
<a href="https://example.com/">Link</a> <https://example.com/|Link>
<a href="mailto:mail@example.com">Mail</a> <mailto:mail@example.com/|Mail>
<a href="#C024BE7LR" /> <#C024BE7LR>
<a href="@U024BE7LH" /> <@U024BE7LH>
<a href="@SAZ94GDB8" /> <!subteam^SAZ94GDB8>
<a href="@here" /> <!here|here>
<a href="@channel" /> <!channel|channel>
<a href="@everyone" /> <!everyone|everyone>

About escape

jsx-slack are making effort to be focusable only to contents of your message. Nevertheless, we may require you to consider escaping contents.

Special characters

We think that anyone never wants to care about special characters for Slack mrkdwn while using jsx-slack. But unfortunately, Slack does not provide how to escape special characters for formatting text. 🤔

The content would break when JSX contents may have mrkdwn special characters like * _ ~ ` >.

<Escape>: Escape special characters

To battle against breaking message, we provide <Escape> component to replace special characters into another similar character.

<Blocks>
  <Section>&gt; *bold* _italic_ ~strikethrough~ `code`</Section>
  <Section>
    <Escape>&gt; *bold* _italic_ ~strikethrough~ `code`</Escape>
  </Section>
</Blocks>

By using <Escape>, please notice that it may change characters in contents. jsx-slack will leave mrkdwn by default to avoid unintended contents interpolation via escape. We recommend using <Escape> only to unpredictable contents like made by users.

Details

> (&gt;) and (U+FF1E) would recognize as blockquote only when it has coming to the beginning of line. If it was found, we will add normally invisible soft hyphen (U+00AD) to the beginning.

Other special chars will replace to another Unicode character whose similar shape.

  • * ➡️ (Asterisk operator: U+2217)
  • ➡️ (Small asterisk: U+FF0A)
  • _ ➡️ ˍ (Modifier letter low macron: U+02CD)
  • _ ➡️ (Paragraphos: U+2E0F)
  • ` ➡️ ˋ (Modifier letter grave accent: U+02CB)
  • ➡️ ˋ (Modifier letter grave accent: U+02CB)
  • ~ ➡️ (Tilde operator: U+223C)

These replacements also will trigger by using corresponded HTML tag. (e.g. * and in the contents of <b> tag)

Exact mode

Some special characters will work only in breaks of words. Take a look this example:

<Blocks>
  <Section>
    Super<i>cali</i>fragilistic<b>expiali</b>docious
  </Section>
</Blocks>

We expect showing the post as follow:

Supercalifragilisticexpialidocious

However, Slack renders as:

Super_cali_fragilistic*expiali*docious

You can deal workaround via SlackJSX.exactMode(true). It can enable formatting forcibly by inserting zero-width space around special chars.

Exact mode is a last resort. We recommend dealing with incorrect rendering by such as inserting spaces around markup elements.

Similar projects

  • slack-jsx - Compose Slack messages from JSX Components instead of writing JSON.

Author

Managed by Speee, Inc. Speee, Inc. (@speee)

  • @yhatt Yuki Hattori (@yhatt) - Maintainer

Licnese

MIT License

You can’t perform that action at this time.