feat(types): add ContainerBlock for block kit #2721

Open
srtaalej opened 10:31am on August 31, 2026 wants to merge 65 Δ into slackapi/node-slack-sdk main from
ale-add-containerblock

Pull Request Overview

  • Opened on August 31, 2026
  • Status Open
  • Commit count 6 with first commit August 31, 2026

Total Delta

65 Total Diff Delta

Open Days

Open 15 weekdays

Test Delta

8 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Fraction of total time Business days Phase
 
0.0 days Authoring 1 commit before pull request opened for review
 
0.0 days Awaiting first review
 
14.5 days Revising work with 5 commits in response to 0 reviews that left 2 comments

Total time for pull request still awaiting merge (longer than repo's target): 14.5 business days

Author avatar

feat(types): add ContainerBlock for block kit

Summary

Adds ContainerBlock (type: "container") to @slack/types per https://docs.slack.dev/reference/block-kit/blocks/container-block/


  • New ContainerBlock interface with all documented properties: title, rich_text_title, subtitle, child_blocks, width, icon, is_collapsible, default_collapsed, has_header_divider

  • New ContainerBlockChildBlock helper union type for the 11 supported child block types

  • Added to the KnownBlock discriminated union

  • Type tests covering happy path, sad path, and KnownBlock assignability

  • Note: the API rejects has_header_divider: true when is_collapsible: true — documented in JSDoc

Example Bolt test app (app.js)

Register a `/container-block` slash command in your test app and use the text argument to test different property combinations:

- `/container-block` — base (title + child_blocks)
- `/container-block width` — adds `width: "wide"`
- `/container-block mrkdwn-subtitle` — subtitle with `mrkdwn` type
- `/container-block has-header-divider` — adds `has_header_divider: true`
- `/container-block default-collapsed` — adds `is_collapsible` + `default_collapsed`
- `/container-block icon` — adds `icon` image element
- `/container-block rich-text-title` — uses `rich_text_title` instead of `title`
- `/container-block all-collapsible` — all props with collapsible
- `/container-block all-divider` — all props with header divider
- `/container-block all` — all compatible properties together

```javascript
import { App, LogLevel } from '@slack/bolt';
import { config } from 'dotenv';

config();

const app = new App({
token: process.env.SLACK_BOT_TOKEN,
socketMode: true,
appToken: process.env.SLACK_APP_TOKEN,
logLevel: LogLevel.DEBUG,
});

app.command('/container-block', async ({ ack, respond, command }) => {
await ack();
const variant = (command.text || '').trim();

const base = {
type: 'container',
title: { type: 'plain_text', text: 'Container Block Test' },
child_blocks: [
{ type: 'section', text: { type: 'mrkdwn', text: 'Section inside a container.' } },
{ type: 'divider' },
{ type: 'section', text: { type: 'mrkdwn', text: 'Another section below the divider.' } },
],
};

if (variant === 'width') {
base.width = 'wide';
} else if (variant === 'mrkdwn-subtitle') {
base.subtitle = { type: 'mrkdwn', text: '*Bold* subtitle' };
} else if (variant === 'has-header-divider') {
base.has_header_divider = true;
} else if (variant === 'default-collapsed') {
base.is_collapsible = true;
base.default_collapsed = true;
} else if (variant === 'icon') {
base.icon = {
type: 'image',
image_url: 'https://api.slack.com/img/blocks/bkb_template_images/plants.png',
alt_text: 'icon',
};
} else if (variant === 'rich-text-title') {
delete base.title;
base.rich_text_title = {
type: 'rich_text',
elements: [
{ type: 'rich_text_section', elements: [{ type: 'text', text: 'Rich Title' }] },
],
};
} else if (variant === 'all-collapsible') {
base.subtitle = { type: 'plain_text', text: 'Collapsible variant' };
base.width = 'wide';
base.icon = {
type: 'image',
image_url: 'https://api.slack.com/img/blocks/bkb_template_images/plants.png',
alt_text: 'icon',
};
base.is_collapsible = true;
} else if (variant === 'all-divider') {
base.subtitle = { type: 'plain_text', text: 'Header divider variant' };
base.width = 'wide';
base.icon = {
type: 'image',
image_url: 'https://api.slack.com/img/blocks/bkb_template_images/plants.png',
alt_text: 'icon',
};
base.has_header_divider = true;
} else if (variant === 'all') {
base.subtitle = { type: 'plain_text', text: 'All compatible properties' };
base.width = 'wide';
base.icon = {
type: 'image',
image_url: 'https://api.slack.com/img/blocks/bkb_template_images/plants.png',
alt_text: 'icon',
};
base.is_collapsible = true;
} else {
base.subtitle = {
type: 'plain_text',
text: 'Base test (try: width, mrkdwn-subtitle, has-header-divider, default-collapsed, icon, rich-text-title, all)',
};
}

await respond({ blocks: [base] });
});

(async () => {
await app.start();
console.log('⚡️ Bolt app is running!');
})();
```

Requirements

Comments Threads Pending Resolution

Resolved Comment Threads

No resolved comments have been left on this PR.