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:
| Preset | Adds |
|---|---|
conservative | Nothing; baseline rich_text path only |
2025-02 | markdownBlock |
2025-08 | tableBlock |
2026-03 | preformattedLanguage, headerLevel |
2026-05 | dataTable |
2026-06 | dataVisualization, 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.