slack-block-builder

repository·main·Indexed 20 days ago

https://github.com/raycharius/slack-block-builder

A lightweight, zero-dependency TypeScript library for declaratively building Slack Block Kit UI. It provides a SwiftUI-inspired chainable syntax to create maintainable, testable, and reusable interactive messages, modals, home tabs, and workflow steps. Features include components for pagination and accordions, markdown helpers (Md), and utility functions for collection transformations.

Tokens
53.1K
Snippets
179
Records
212
Agent score
69%

What's inside slack-block-builder

  1. Generate API payloads using buildToJSON()

    main

    To generate the complete JSON payload required for specific Slack API methods, call .buildToJSON() on your Surface object. The output format depends on the type of surface used:

    Surface TypeMethodTarget Slack API Parameter/Method
    Message.buildToJSON()Full payload for chat.postMessage
    Modal.buildToJSON()Full payload for the view parameter in views.open, views.update, and views.push
    HomeTab.buildToJSON()Full payload for the view parameter in views.publish

    If you only need the UI components without the surrounding API metadata, use the .getBlocks() method instead to retrieve an array of Blocks.

    // To get a full API payload:
    const payload = Modal.buildToJSON();
    
    // To get only the UI blocks:
    const blocks = Modal.getBlocks();
  2. How Block Builder works and its design patterns

    main

    Block Builder uses a builder pattern inspired by SwiftUI to create maintainable code that reflects the structure of Slack Block Kit UI.

    Core Concepts:

    • Builders: When you instantiate an object (like a Message), you receive a builder instance. This instance provides setter methods to configure properties.
    • Chaining: Setter methods return the builder instance, allowing you to chain calls together to define the object's structure.
    • Instantiation: Functions used to create objects can accept a parameters object to set initial properties, but only for primitive properties. You cannot pass Blocks or Elements directly into the instantiation parameters; instead, you should use the builder's methods to maintain a declarative structure.
    • Build Methods: To convert the builder into a Slack API-compatible format, you must call a build method. The most common method is buildToJSON(). Calling a build method mutates the object's properties according to Slack API specifications to produce the final output.
    import { Message, Blocks } from 'slack-block-builder';
    
    // Example of chaining methods
    const myMessage = Message()
      .channel('C12345')
      .text('Hello')
      .buildToJSON();
    
    // Example of using params and chaining
    const myMessageAlt = Message({ channel: 'C12345', text: 'Hello' })
      .buildToJSON();
  3. Use the Paginator component

    main

    The Paginator component automates paginated UI. It provides nextButtonText, previousButtonText, and pageCountText (which accepts a function with { page, totalPages }). It also provides an actionId callback that receives { page, perPage, totalPages, offset, totalItems } to help you generate the correct payload for the next interaction.

    import { Modal, Blocks, Elements, Paginator } from 'slack-block-builder';
    
    export default ({ tasks, totalTasks, page, perPage }) => Modal({ title: 'Open Tasks' })
      .blocks(
        Blocks.Section({ text: 'Hi! :wave: And welcome to the FAQ section! Take a look around and if you don\'t find what you need, feel free to open an issue on GitHub.' }),
        Blocks.Section({ text: `You currently have *${totalTasks} open task(s)*:` }),
        Paginator({
          perPage,
          items: tasks,
          totalItems: totalTasks,
          page: page || 1,
          actionId: ({ page, offset }) => JSON.stringify({ action: 'render-tasks', page, offset }),
          blocksForEach: ({ item }) => [
            Blocks.Divider(),
            Blocks.Section({ text: `*${item.title}*` })
              .accessory(
                Elements.Button({ text: 'View Details' })
                  .actionId('view-details')
                  .value(item.id.toString())),
            Blocks.Section({ text: `*Due Date:* ${getDate(item.dueDate)}` }),
          ],
        }).getBlocks())
      .close('Done')
      .buildToJSON();
  4. What are Elements in Slack Block Kit

    main
    In the Slack Block Kit framework, Elements are components responsible for gathering user feedback and interaction. They include various types of inputs, select menus, buttons, and other interactive components. When users interact with these elements (e.g., clicking a button or submitting a modal), your application receives payloads containing the action or the submitted form values.
  5. Access Slack Blocks using the Blocks object

    main

    Block Builder supports all components from the Slack Block Kit framework. You can access these components through the top-level Blocks object. To create a specific block, call the corresponding method on the Blocks object (for example, Blocks.Actions() to create an Actions block).

    import { Message, Blocks } from 'slack-block-builder';
    
    // Example: Creating an Actions block
    const actionsBlock = Blocks.Actions();
  6. Understand Slack Block Kit Surfaces

    main

    In Slack development, a Surface is the canvas where your app's content is displayed. You compose these surfaces using Blocks and Elements. Block Builder provides specialized classes for the different types of surfaces available in Slack:

    • Messages: Sent to users or channels via chat.postMessage. Can be plain text or interactive blocks.
    • Modals: Dialog boxes triggered by user actions (like Slash Commands or button clicks), deployed via view.open, view.update, or view.push.
    • Home Tabs: An app's persistent landing page, published via view.publish.
    • Workflow Steps: Modal views specifically designed for use within Slack workflows.
  7. What are Bits in Block Builder

    main

    In Block Builder, Bits is a top-level object that contains specific composition elements from the Slack Block Kit that do not follow the standard block-based hierarchy. While most Slack Block Kit components are accessed through standard builders, Bits is used to create:

    • Attachment
    • Options
    • Option Groups
    • Confirmation Dialogs

    Note that other types like Markdown, Plain-Text, and FilterType are handled automatically in the background when a view is compiled and do not require manual access via Bits.

  8. Use the Accordion component

    main

    The Accordion component creates expandable/collapsible UI items. It calculates the next state for you and provides it via the actionId callback.

    Key options:

    • collapseOnExpand: If true, only one item can be expanded at a time.
    • expandButtonText / collapseButtonText: Customizes button text.
    • isExpandable: Controls visibility of the toggle button for an item.
    • items: The data array.
    • expandedItems: An array of currently expanded item identifiers.
    • titleText: Function to render the header text.
    • actionId: Function to generate the action_id for the toggle buttons.
    • blocksForExpanded: Function to return the blocks shown when an item is expanded.
    import { Modal, Blocks, Accordion } from 'slack-block-builder';
    
    export default ({ faqs, expandedItems }) => Modal({ title: 'FAQ' })
      .blocks(
        Blocks.Section({ text: 'Hi! :wave: And welcome to the FAQ section! Take a look around and if you don\'t find what you need, feel free to open an issue on GitHub.'}),
        Blocks.Divider(),
        Accordion({
          items: faqs,
          expandedItems: expandedItems || [],
          collapseOnExpand: true,
          titleText: ({ item }) => `*${item.question}*`,
          actionId: ({ expandedItems }) => JSON.stringify({ action: 'render-faqs', expandedItems }),
          blocksForExpanded: ({ item }) => [
           Blocks.Section({ text: `${item.answer}` }),
          ],
        }).getBlocks())
      .close('Done')
      .buildToJSON();
  9. How the Accordion component works

    main

    The Accordion component generates a UI for expandable and collapsible content in Slack. It works by taking an array of data items and using callback functions to determine how to render titles, how to handle state transitions via action_id, and what blocks to show when an item is expanded.

    Key behaviors:

    • State Management: It uses the expandedItems array (an array of integers representing indexes) to track which items are currently open.
    • Interactivity: When a user clicks an expand/collapse button, the actionId function generates a string that is sent to your backend. Your backend should use this string to calculate the next state and re-render the accordion.
    • Single Expansion: If collapseOnExpand is set to true, expanding a new item will automatically collapse any currently expanded item.

    To use it, call Accordion(params).getBlocks() to retrieve the blocks for your Slack message or modal.

    import { Accordion } from 'slack-block-builder';
    
    const accordion = Accordion(params);
    const blocks = accordion.getBlocks();
  10. When to use an Attachment Collection

    main

    While the Message object in Block Builder can handle both UI representation and message behavior configuration, you may want to keep these concerns separate in your application. Use AttachmentCollection to define attachments independently of your main block structure. This is particularly useful when integrating with the Slack WebClient, where you can pass blocks and attachments as separate top-level keys in the API payload.

    import { BlockCollection, AttachmentCollection } from 'slack-block-builder';
    import { WebClient } from '@slack/web-api';
    
    const client = new WebClient(process.env.SLACK_TOKEN);
    
    client.chat.postMessage({
      channel: 'ABCDEFG',
      text: 'Hello, my dear, sweet world!',
      blocks: BlockCollection( /* Pass in blocks */ ),
      attachments: AttachmentCollection( /* Pass in attachments */ ),
    })
    .then((response) => console.log(response))
    .catch((error) => console.log(error));
  11. When to use a Block Collection

    main

    While Model, Message, WorkflowStep, and HomeTab objects in Block Builder can handle both UI representation and surface configuration, you should use BlockCollection when you want to keep your UI representation and surface configuration separate in your application. This is particularly useful when passing blocks directly to external clients like Slack's WebClient.

    import { BlockCollection, AttachmentCollection, Blocks } from 'slack-block-builder';
    import { WebClient } from '@slack/web-api';
    
    const client = new WebClient(process.env.SLACK_TOKEN);
    
    client.chat.postMessage({
      channel: 'ABCDEFG',
      text: 'Hello, my dear, sweet world!',
      blocks: BlockCollection( /* Pass in blocks */ ),
      attachments: AttachmentCollection( /* Pass in attachments */ ),
    })
    .then((response) => console.log(response))
    .catch((error) => console.log(error));
  12. Understanding Surface object immutability and building

    main

    In Block Builder, all UIs start as a Surface object (Message, Modal, or HomeTab).

    Key behaviors:

    • Immutability: Once a build method (like buildToJSON, buildToObject, etc.) is called, the object becomes immutable. Subsequent calls to any build method will not mutate the object; they will simply return the result generated during the first build call.
    • Compatibility: The build process automatically performs necessary mutations to ensure the data structure is compatible with the Slack API.