Slack compatibility

Slack rolled out its newer block types (markdown, table, data_table, data_visualization, container, …) over time, and not every surface supports all of them yet. Capability profiles tell slackmark which block types the destination accepts; anything gated off falls back to the closest supported form, with a degradation record.

The default is latest, every capability on. If a message comes back with an invalid_blocks error on some surface, pick the dated preset matching what that surface supports instead of hand-filtering blocks.

Presets

Presets are cumulative and keyed to Slack changelog dates:

PresetAdds
conservativeNothing; baseline rich_text path only
2025-02markdownBlock
2025-08tableBlock
2026-03preformattedLanguage, headerLevel
2026-05dataTable
2026-06dataVisualization, containerBlock
latest (default)Everything
import { convert } from "slackmark";

const result = await convert(markdown, { capabilities: "2025-08" });

conservative is the safe floor: output uses only rich_text, header, section, image, context, and divider blocks, which every Block Kit surface renders. See the capability presets reference for what each flag gates and its fallback.

Here is one document under both extremes, default latest on the left and capabilities: "conservative" on the right:

#### Rollout status

- [x] Canary
- [ ] Full fleet

```ts
await deploy("prod");
```
Block Kit JSON
#### Rollout status

- [x] Canary
- [ ] Full fleet

```ts
await deploy("prod");
```
Block Kit JSON

Three things changed. The header dropped its level, the task list became unicode checkboxes in a rich text list, and the code fence lost its language tag. The first two are visible in the render; the third is a field in the payload, and all three are named in the degradations under the right-hand figure.

Fine-grained flags and overrides

Pass a flags object instead of a preset name, or layer capabilityOverrides on top of any base. A partial flags object is merged over latest:

import { convert } from "slackmark";

// Preset plus one override.
await convert(markdown, {
  capabilities: "2026-03",
  capabilityOverrides: { tableBlock: false },
});

// Partial flags merged over `latest`.
await convert(markdown, {
  capabilities: { dataVisualization: false },
});

Unknown preset names throw a ConfigurationError when the conversion runs, so a typo will not silently enable every capability.