slack-blocks-to-jsx 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,772 @@
1
+ # slack-blocks-to-jsx — Knowledge Base
2
+
3
+ > Book-style knowledge base for the `slack-blocks-to-jsx` React library.
4
+ > Use the index below to jump to the section you need — do **not** read linearly.
5
+ > When code or architecture changes, update the relevant chapter here in the same PR.
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ | Ch. | Section | Topic |
12
+ | --- | --- | --- |
13
+ | 0 | [§0](#chapter-0--how-to-use-this-knowledge-base) | **How to use this knowledge base** |
14
+ | 0 | [§0.1](#01-what-this-document-is) | What this document is |
15
+ | 0 | [§0.2](#02-how-to-read-it-effectively) | How to read it effectively |
16
+ | 0 | [§0.3](#03-when-to-update-it) | When to update it |
17
+ | 0 | [§0.4](#04-how-to-update-it) | How to update it |
18
+ | 0 | [§0.5](#05-style-rules) | Style rules |
19
+ | 0 | [§0.6](#06-who-maintains-this) | Who maintains this |
20
+ | 1 | [§1](#chapter-1--overview) | Overview, identity, links |
21
+ | 1 | [§1.1](#11-package-identity) | Package identity |
22
+ | 1 | [§1.2](#12-what-it-does) | What it does |
23
+ | 1 | [§1.3](#13-repo--publish-links) | Repo and publish links |
24
+ | 2 | [§2](#chapter-2--installation--runtime-requirements) | Installation & runtime requirements |
25
+ | 2 | [§2.1](#21-install-command) | Install command |
26
+ | 2 | [§2.2](#22-peer-dependencies) | Peer dependencies |
27
+ | 2 | [§2.3](#23-runtime-dependencies) | Runtime dependencies |
28
+ | 2 | [§2.4](#24-css-import-requirement) | CSS import requirement |
29
+ | 3 | [§3](#chapter-3--public-api) | Public API |
30
+ | 3 | [§3.1](#31-exports-from-srcindexts) | Exports from `src/index.ts` |
31
+ | 3 | [§3.2](#32-message-component-props) | `Message` component props |
32
+ | 3 | [§3.3](#33-header-component) | `Header` component |
33
+ | 3 | [§3.4](#34-blockwrapper-component) | `BlockWrapper` component |
34
+ | 3 | [§3.5](#35-component-dispatcher) | Component dispatcher |
35
+ | 4 | [§4](#chapter-4--blocks-reference) | Blocks reference |
36
+ | 4 | [§4.1](#41-layout--structure-blocks) | Layout & structure blocks (section, divider, header) |
37
+ | 4 | [§4.2](#42-media-blocks) | Media blocks (image, video, file) |
38
+ | 4 | [§4.3](#43-interactive-blocks) | Interactive blocks (actions, input, context_actions) |
39
+ | 4 | [§4.4](#44-content-blocks) | Content blocks (context, table, markdown) |
40
+ | 4 | [§4.5](#45-ai-workflow-blocks) | AI/workflow blocks (plan, task_card) |
41
+ | 4 | [§4.6](#46-status--rich-container-blocks-added-v110--slack-2026-04-16) | Status & rich-container blocks (alert, card, carousel) |
42
+ | 4 | [§4.7](#47-rich-text-block) | Rich text block (deep dive) |
43
+ | 5 | [§5](#chapter-5--elements-reference) | Elements reference |
44
+ | 5 | [§5.1](#51-button-family) | Button family (button, workflow_button) |
45
+ | 5 | [§5.2](#52-single-select-menus) | Single-select menus |
46
+ | 5 | [§5.3](#53-multi-select-menus) | Multi-select menus |
47
+ | 5 | [§5.4](#54-choice-elements) | Choice elements (radio_buttons, checkboxes) |
48
+ | 5 | [§5.5](#55-text--input-elements) | Text & input elements |
49
+ | 5 | [§5.6](#56-date--time-pickers) | Date & time pickers |
50
+ | 5 | [§5.7](#57-file-upload--rich-text-input) | File upload & rich text input |
51
+ | 5 | [§5.8](#58-display-elements) | Display elements (image, overflow, feedback, icon, url) |
52
+ | 6 | [§6](#chapter-6--type-system) | Type system |
53
+ | 6 | [§6.1](#61-typeslayoutts) | `types/layout.ts` — block union |
54
+ | 6 | [§6.2](#62-typeselementsts) | `types/elements.ts` — element union |
55
+ | 6 | [§6.3](#63-typesobjectsts) | `types/objects.ts` — composition objects |
56
+ | 6 | [§6.4](#64-typesrich_text_elementts) | `types/rich_text_element.ts` |
57
+ | 7 | [§7](#chapter-7--state--render-hooks) | State & render hooks |
58
+ | 7 | [§7.1](#71-data-prop-users-channels-usergroups) | `data` prop |
59
+ | 7 | [§7.2](#72-hooks-prop-custom-renderers) | `hooks` prop |
60
+ | 7 | [§7.3](#73-useglobaldata-hook) | `useGlobalData` internal hook |
61
+ | 8 | [§8](#chapter-8--rendering-pipeline) | Rendering pipeline |
62
+ | 8 | [§8.1](#81-dispatcher-flow) | Dispatcher flow |
63
+ | 8 | [§8.2](#82-markdown-parser) | Markdown parser (Yozora + custom Slack tokenizers) |
64
+ | 8 | [§8.3](#83-emoji-parser) | Emoji parser |
65
+ | 8 | [§8.4](#84-composition-objects) | Composition objects (text, confirm_dialog) |
66
+ | 9 | [§9](#chapter-9--styling) | Styling |
67
+ | 9 | [§9.1](#91-tailwind-scope) | Tailwind scope |
68
+ | 9 | [§9.2](#92-dark-mode) | Dark mode |
69
+ | 9 | [§9.3](#93-css-build-pipeline) | CSS build pipeline |
70
+ | 10 | [§10](#chapter-10--build--distribution) | Build & distribution |
71
+ | 10 | [§10.1](#101-tsup-config) | tsup config |
72
+ | 10 | [§10.2](#102-tsconfig) | tsconfig |
73
+ | 10 | [§10.3](#103-scripts) | npm scripts |
74
+ | 10 | [§10.4](#104-dist-output) | dist/ output |
75
+ | 10 | [§10.5](#105-npm-publish-hygiene) | npm publish hygiene |
76
+ | 11 | [§11](#chapter-11--release-process) | Release process |
77
+ | 12 | [§12](#chapter-12--test-fixtures) | Test fixtures |
78
+ | 13 | [§13](#chapter-13--complete-file-tree) | Complete file tree |
79
+ | 14 | [§14](#chapter-14--architectural-conventions) | Architectural conventions |
80
+ | 15 | [§15](#chapter-15--change-log-of-this-doc) | Change log of this doc |
81
+
82
+ ---
83
+
84
+ ## Chapter 0 — How to use this knowledge base
85
+
86
+ ### 0.1 What this document is
87
+
88
+ A chapter-indexed field manual covering the entire `slack-blocks-to-jsx` codebase — architecture, public API, every block and element, the type system, the rendering pipeline, build tooling, and release process. It is **not** a tutorial and **not** the source of truth (the code is). It is the fastest way to answer "where does X live?" and "how does X work?" without re-reading source.
89
+
90
+ Think of it as a reference book, not a textbook. Look up, don't read through.
91
+
92
+ ### 0.2 How to read it effectively
93
+
94
+ 1. **Always start at the TOC.** Never scroll the body looking for a topic — use the index.
95
+ 2. **Jump by anchor link.** Every TOC entry is clickable and drops you at the exact section.
96
+ 3. **Read the smallest unit needed.** A section is written to stand on its own; you rarely need the surrounding chapter.
97
+ 4. **Follow the cross-references.** `see §4.7` means "jump to Chapter 4, Section 7" — do that instead of searching again.
98
+ 5. **Gap = signal.** If the TOC doesn't answer your question, the doc is stale. Fix it after you learn the answer from the code (see §0.3–§0.4).
99
+
100
+ Quick routing by task:
101
+
102
+ | If you're asking… | Go to |
103
+ | --- | --- |
104
+ | What file is X in? | Ch. 13 (file tree) |
105
+ | What does the public API export? | Ch. 3 |
106
+ | How is block/element X implemented? | Ch. 4 / Ch. 5 |
107
+ | What's the type of X? | Ch. 6 |
108
+ | How do mentions / mrkdwn get parsed? | Ch. 8 §8.2 |
109
+ | How does styling / dark mode work? | Ch. 9 |
110
+ | How do I ship a change? | Ch. 10 / Ch. 11 |
111
+ | What rule governs X? | Ch. 14 (conventions) |
112
+
113
+ ### 0.3 When to update it
114
+
115
+ Update the knowledge base in the **same PR** as the code change, never in a follow-up. Treat these as mandatory triggers:
116
+
117
+ - A block or element is added, renamed, removed, or has its props changed → update Ch. 4 / Ch. 5 + Ch. 6.
118
+ - A public export is added or removed from `src/index.ts` → update Ch. 3.
119
+ - Build tooling changes (`tsup.config.ts`, `tsconfig.json`, `tailwind.config.js`, `postcss.config.js`) → update Ch. 9 / Ch. 10.
120
+ - Peer deps, runtime deps, Node/React target shifts → update Ch. 2.
121
+ - A file moves, is deleted, or a folder is renamed → update Ch. 13.
122
+ - The release or publish process changes → update Ch. 11.
123
+ - A new architectural rule emerges (or an existing one changes) → update Ch. 14.
124
+ - A new test fixture is added under `test-blocks/` → update Ch. 12.
125
+
126
+ If the KB and code diverge by even one commit, the KB stops being trustable. Don't let that happen.
127
+
128
+ ### 0.4 How to update it
129
+
130
+ 1. Find the affected chapter via the TOC (do **not** grep the whole file — use the index).
131
+ 2. Edit only the relevant section. Keep style consistent with §0.5.
132
+ 3. **Add** a new section if needed: give it a number (`§4.8`), add a TOC row, and match the existing anchor format (lowercase, hyphen-separated, strip punctuation).
133
+ 4. If you break an existing anchor (renamed a heading), search for `§X.Y` cross-references and update them.
134
+ 5. Log the change in **Ch. 15 — Change log of this doc** with: date, author, one-line summary.
135
+ 6. Review the diff: TOC row, body section, change log row — all three should appear.
136
+
137
+ ### 0.5 Style rules
138
+
139
+ - **Tables** for enumerations ("list of X with properties Y and Z").
140
+ - **Prose** for concepts and rationale.
141
+ - **Code blocks** for type signatures, short examples, prop shapes — never for paragraphs.
142
+ - **Backticks** for file paths, identifiers, tool names, package names.
143
+ - **Don't duplicate source.** Summarize intent and link to the file path; the code is the truth.
144
+ - **Self-contained sections.** Assume the reader lands mid-book; don't say "as we saw above" without a `§` reference.
145
+ - **Every new heading gets a TOC row.**
146
+
147
+ ### 0.6 Who maintains this
148
+
149
+ Whoever makes the code change owns the KB update for that change. When Claude/Cowork is driving, I update the KB in the same turn as the code edit and log it in Ch. 15. If you ever spot KB drift, flag it and I'll reconcile.
150
+
151
+ ---
152
+
153
+ ## Chapter 1 — Overview
154
+
155
+ ### 1.1 Package identity
156
+
157
+ | Field | Value |
158
+ | --- | --- |
159
+ | Name | `slack-blocks-to-jsx` |
160
+ | Current version | `1.0.3` (see `package.json`) |
161
+ | License | MIT |
162
+ | Author | Manish Panwar (`codeemash@gmail.com`) |
163
+ | Package manager | pnpm |
164
+ | Node target | ES2016 via tsup |
165
+ | Keywords | `slack`, `slack-blocks`, `slack-blocks-to-jsx`, `slack-blocks-to-react`, `slack-blocks-to-react-comp` |
166
+
167
+ ### 1.2 What it does
168
+
169
+ Renders a Slack Block Kit `blocks[]` payload as React/JSX with pixel-close styling to the real Slack UI. Used to preview Slack messages, modals, and interactive surfaces inside any React app. Ships CJS, ESM, type declarations, and a single compiled CSS file.
170
+
171
+ ### 1.3 Repo and publish links
172
+
173
+ - Repository: `https://github.com/themashcodee/slack-blocks-to-jsx.git`
174
+ - npm: `https://www.npmjs.com/package/slack-blocks-to-jsx`
175
+
176
+ ---
177
+
178
+ ## Chapter 2 — Installation & runtime requirements
179
+
180
+ ### 2.1 Install command
181
+
182
+ ```bash
183
+ npm install slack-blocks-to-jsx
184
+ # or
185
+ pnpm add slack-blocks-to-jsx
186
+ ```
187
+
188
+ ### 2.2 Peer dependencies
189
+
190
+ ```jsonc
191
+ {
192
+ "react": "^17 || ^18 || ^19",
193
+ "react-dom": "^17 || ^18 || ^19"
194
+ }
195
+ ```
196
+
197
+ `.npmrc` enables `auto-install-peers=true`. Since v1.0.1, `react-markdown` and `remark-gfm` are **externalized**, so downstream bundlers (Next.js etc.) must be able to resolve them.
198
+
199
+ ### 2.3 Runtime dependencies
200
+
201
+ | Package | Version | Purpose |
202
+ | --- | --- | --- |
203
+ | `node-emoji` | `^2.1.3` | Convert `:emoji_name:` → unicode |
204
+ | `react-markdown` | `^9.1.0` | Renders the `markdown` block (standard GFM) |
205
+ | `remark-gfm` | `^4.0.1` | GFM plugin for the above |
206
+
207
+ ### 2.4 CSS import requirement
208
+
209
+ Users must import the compiled stylesheet once in their app entry:
210
+
211
+ ```ts
212
+ import "slack-blocks-to-jsx/dist/style.css"
213
+ ```
214
+
215
+ All classes are prefixed under `#slack_blocks_to_jsx` (see §9.1) so they do not leak.
216
+
217
+ ---
218
+
219
+ ## Chapter 3 — Public API
220
+
221
+ ### 3.1 Exports from `src/index.ts`
222
+
223
+ ```ts
224
+ export * from "./message" // Message, MessageProps
225
+ export * from "./types" // Block, Element, TextObject, etc.
226
+ ```
227
+
228
+ Nothing else is intentionally public.
229
+
230
+ ### 3.2 `Message` component props
231
+
232
+ File: `src/message.tsx`.
233
+
234
+ ```ts
235
+ type MessageProps = {
236
+ blocks: Block[]
237
+ logo: string
238
+ name: string
239
+ time?: Date // defaults to new Date()
240
+ className?: string
241
+ style?: React.CSSProperties
242
+ theme?: "light" | "dark"
243
+ showBlockKitDebug?: boolean // shows a "Open in Block Kit Builder" link
244
+ unstyled?: boolean // default false; skip shipping classes
245
+ withoutWrapper?: boolean // skip the outer container + header
246
+ data?: {
247
+ users?: { id: string; name: string }[]
248
+ channels?: { id: string; name: string }[]
249
+ user_groups?: { id: string; name: string }[]
250
+ }
251
+ hooks?: GlobalStore["hooks"] // see §7.2
252
+ }
253
+ ```
254
+
255
+ Rendered DOM shape:
256
+
257
+ ```html
258
+ <div id="slack_blocks_to_jsx" data-theme="light|dark">
259
+ <Header name={name} time={time} />
260
+ <div class="slack_blocks_to_jsx--blocks">
261
+ {blocks.map(b => <BlockWrapper>{getBlockComponent(b)}</BlockWrapper>)}
262
+ </div>
263
+ </div>
264
+ ```
265
+
266
+ ### 3.3 `Header` component
267
+
268
+ File: `src/header.tsx`. Renders app name (bold) + `APP` badge + `HH:mm` timestamp.
269
+
270
+ ### 3.4 `BlockWrapper` component
271
+
272
+ File: `src/block_wrapper.tsx`. Thin wrapper that sets dark-mode text color and `break-words`.
273
+
274
+ ### 3.5 Component dispatcher
275
+
276
+ File: `src/components/index.tsx`.
277
+
278
+ ```ts
279
+ getBlockComponent(block: Block): ReactElement | null
280
+ getElementComponent(element: Element): ReactElement | null
281
+ ```
282
+
283
+ Switch on `block.type` / `element.type`. Returns `null` for unsupported types, so unknown Slack block additions won't crash — they just render nothing.
284
+
285
+ ---
286
+
287
+ ## Chapter 4 — Blocks reference
288
+
289
+ All block components live in `src/components/blocks/`. 17 block types are supported.
290
+
291
+ ### 4.1 Layout & structure blocks
292
+
293
+ | Block | File | Key props | Notes |
294
+ | --- | --- | --- | --- |
295
+ | `section` | `section.tsx` | `text`, `fields`, `accessory`, `expand`, `block_id` | Workhorse block; accessory can stack vertically (see `utils/is_accessory_stacked.ts`) |
296
+ | `divider` | `divider.tsx` | `block_id` | Horizontal rule |
297
+ | `header` | `header.tsx` | `text` (plain_text, ≤150), `block_id` | Big bold title |
298
+
299
+ ### 4.2 Media blocks
300
+
301
+ | Block | File | Key props |
302
+ | --- | --- | --- |
303
+ | `image` | `image.tsx` | `image_url` or `slack_file`, `alt_text`, `title`, `block_id`; collapsible title |
304
+ | `video` | `video.tsx` | `video_url`, `thumbnail_url`, `title`, `alt_text`, `title_url`, `block_id`; collapsible; YouTube/Vimeo friendly |
305
+ | `file` | `file.tsx` | `external_id`, `source="remote"`, `block_id` |
306
+
307
+ ### 4.3 Interactive blocks
308
+
309
+ | Block | File | Key props |
310
+ | --- | --- | --- |
311
+ | `actions` | `actions.tsx` | `elements[]` (≤25) — routes to `getElementComponent` for every supported element type |
312
+ | `input` | `input.tsx` | `label`, `element` (any input element), `optional`, `hint`, `dispatch_action_config` |
313
+ | `context_actions` | `context_actions.tsx` | `elements[]` (feedback/icon buttons for AI-response affordances) |
314
+
315
+ ### 4.4 Content blocks
316
+
317
+ | Block | File | Notes |
318
+ | --- | --- | --- |
319
+ | `context` | `context.tsx` | Up to 10 mixed text + image elements |
320
+ | `table` | `table.tsx` | `cells: Cell[][]`, optional `size`; cell alignment; bordered |
321
+ | `markdown` | `markdown_block.tsx` | Renders **standard** GFM via `react-markdown` + `remark-gfm`; tables, task lists, code blocks, syntax highlight |
322
+
323
+ ### 4.5 AI/workflow blocks
324
+
325
+ | Block | File | Key props |
326
+ | --- | --- | --- |
327
+ | `plan` | `plan.tsx` | `title: string`, `tasks: TaskCardBlock[]` — sequential progress UI |
328
+ | `task_card` | `task_card.tsx` | `task_id`, `title`, `status: pending \| in_progress \| complete \| error`, `details?`, `output?`, `sources?` |
329
+
330
+ ### 4.6 Status & rich-container blocks (added v1.1.0 — Slack 2026-04-16)
331
+
332
+ | Block | File | Key props | Notes |
333
+ | --- | --- | --- | --- |
334
+ | `alert` | `alert.tsx` | `text: TextObject`, `level?: "default" \| "info" \| "warning" \| "error" \| "success"`, `block_id?` | Single-line status banner. Each level gets its own icon and tinted border/background that adapts to light/dark theme. `role="status"` for screen readers. Unknown level falls back to `default`. |
335
+ | `card` | `card.tsx` | `title?`, `subtitle?`, `body?`, `hero_image?: CardImage`, `icon?: CardImage`, `actions?: ButtonElement[]` (≤5), `block_id?`; supports `inCarousel` prop when nested | Rich container. Actions reuse the existing `ButtonElement` renderer (so `primary`/`danger` styles and `confirm` dialogs work for free). Hero image renders in a 16:9 frame above the title; icon renders inline next to the title. |
336
+ | `carousel` | `carousel.tsx` | `elements: CardBlock[]` (1–10), `block_id?` | Horizontally-scrollable gallery using CSS scroll-snap. Slides cap at 10; extra elements are dropped silently. Each slide passes `inCarousel` to the `Card` component for a fixed-width variant. |
337
+
338
+ ### 4.7 Rich text block
339
+
340
+ Folder: `src/components/blocks/rich_text/`. The most involved block by far.
341
+
342
+ Dispatcher: `rich_text.tsx` switches on each entry of `elements: RichTextBlockElement[]` and delegates to:
343
+
344
+ - `rich_text_section.tsx` — text elements (text, link, emoji, user, usergroup, channel, broadcast, color, preformatted)
345
+ - `rich_text_list_wrapper.tsx` — ordered/unordered lists, nesting
346
+ - `rich_text_section_color.tsx` — colored background text
347
+ - `rich_text_section_emoji.tsx` — emoji + skin tone
348
+ - `rich_text_section_link.tsx`
349
+ - `rich_text_section_user.tsx`, `..._usergroup.tsx`, `..._channel.tsx`, `..._broadcast.tsx`
350
+
351
+ Each text sub-element supports a `style: { code?, bold?, italic?, strike? }` map. Broadcasts are `@channel`, `@here`, `@everyone`.
352
+
353
+ ---
354
+
355
+ ## Chapter 5 — Elements reference
356
+
357
+ All element components live in `src/components/elements/`. The dispatcher is `getElementComponent`.
358
+
359
+ ### 5.1 Button family
360
+
361
+ | Element | File |
362
+ | --- | --- |
363
+ | `button` | `button_element.tsx` — `text`, `action_id`, `url`, `value`, `style: primary\|danger`, `confirm`, `accessibility_label` |
364
+ | `workflow_button` | `workflow_button_element.tsx` — triggers a Slack workflow; supports `primary`/`danger` styles |
365
+
366
+ ### 5.2 Single-select menus
367
+
368
+ | Element | File | Notes |
369
+ | --- | --- | --- |
370
+ | `static_select` | `static_select_element.tsx` | `options[]` or `option_groups[]`, `initial_option`, `placeholder`, `confirm` |
371
+ | `external_select` | `external_select_element.tsx` | `min_query_length` |
372
+ | `users_select` | `users_select_element.tsx` | `initial_user` |
373
+ | `conversations_select` | `conversations_select_element.tsx` | `filter`, `default_to_current_conversation` |
374
+ | `channels_select` | `channels_select_element.tsx` | `response_url_enabled` |
375
+
376
+ ### 5.3 Multi-select menus
377
+
378
+ | Element | File |
379
+ | --- | --- |
380
+ | `multi_static_select` | `multi_static_select_element.tsx` |
381
+ | `multi_external_select` | `multi_external_select_element.tsx` |
382
+ | `multi_users_select` | `multi_users_select_element.tsx` |
383
+ | `multi_conversations_select` | `multi_conversations_select_element.tsx` |
384
+ | `multi_channels_select` | `multi_channels_select_element.tsx` |
385
+
386
+ All support `max_selected_items` and `confirm`.
387
+
388
+ ### 5.4 Choice elements
389
+
390
+ | Element | File |
391
+ | --- | --- |
392
+ | `radio_buttons` | `radio_buttons_element.tsx` — `options[]`, `initial_option`, `confirm` |
393
+ | `checkboxes` | `checkboxes_element.tsx` — `options[]`, `initial_options[]`, `confirm` |
394
+
395
+ ### 5.5 Text & input elements
396
+
397
+ | Element | File |
398
+ | --- | --- |
399
+ | `plain_text_input` | `plain_text_input.tsx` — `multiline`, `min_length`, `max_length`, `dispatch_action_config` |
400
+ | `email_text_input` | `email_input_element.tsx` |
401
+ | `url_text_input` | `url_input_element.tsx` |
402
+ | `number_input` | `number_input_element.tsx` — `is_decimal_allowed`, `min_value`, `max_value` |
403
+
404
+ ### 5.6 Date & time pickers
405
+
406
+ | Element | File | Value format |
407
+ | --- | --- | --- |
408
+ | `datepicker` | `date_picker_element.tsx` | `YYYY-MM-DD` |
409
+ | `timepicker` | `time_picker_element.tsx` | `HH:mm`, optional `timezone` |
410
+ | `datetimepicker` | `datetime_picker_element.tsx` | Unix timestamp |
411
+
412
+ These are the elements that trigger vertical stacking when used as section accessories (see `utils/is_accessory_stacked.ts`).
413
+
414
+ ### 5.7 File upload & rich text input
415
+
416
+ | Element | File |
417
+ | --- | --- |
418
+ | `file_input` | `file_input_element.tsx` — `filetypes[]`, `max_files` |
419
+ | `rich_text_input` | `rich_text_input_element.tsx` — `initial_value`, `placeholder` |
420
+
421
+ ### 5.8 Display elements
422
+
423
+ | Element | File | Purpose |
424
+ | --- | --- | --- |
425
+ | `image` | `image_element.tsx` | Display-only image; `image_url` or `slack_file` |
426
+ | `overflow` | `overflow_menu_element.tsx` | 3-dot menu, up to 5 options |
427
+ | `feedback_buttons` | `feedback_buttons_element.tsx` | thumbs up / down |
428
+ | `icon_button` | `icon_button_element.tsx` | small icon action (e.g. trash) |
429
+ | `url` | `url_source_element.tsx` | Clickable URL reference for task cards |
430
+
431
+ ---
432
+
433
+ ## Chapter 6 — Type system
434
+
435
+ Types live in `src/types/` and are re-exported from `index.ts`.
436
+
437
+ ### 6.1 `types/layout.ts`
438
+
439
+ Defines the `Block` union: `ActionsBlock | AlertBlock | CardBlock | CarouselBlock | ContextBlock | ContextActionsBlock | DividerBlock | FileBlock | HeaderBlock | ImageBlock | InputBlock | MarkdownBlock | PlanBlock | SectionBlock | TableBlock | TaskCardBlock | VideoBlock | RichTextBlock`. Each interface documents props with JSDoc pointing to Slack's official API docs.
440
+
441
+ Added in v1.1.0 to cover Slack's 2026-04-16 block launch:
442
+
443
+ - `AlertLevel = "default" | "info" | "warning" | "error" | "success"`
444
+ - `AlertBlock = { type: "alert"; text: TextObject; level?: AlertLevel; block_id? }`
445
+ - `CardImage = { image_url?: string; slack_file?: SlackFileObject; alt_text: string }` (shared helper for hero + icon)
446
+ - `CardBlock = { type: "card"; title?; subtitle?; body?; hero_image?: CardImage; icon?: CardImage; actions?: ButtonElement[]; block_id? }`
447
+ - `CarouselBlock = { type: "carousel"; elements: CardBlock[]; block_id? }`
448
+
449
+ ### 6.2 `types/elements.ts`
450
+
451
+ Defines the `Element` union for all 28 interactive/display elements. Every element carries optional `action_id` and `confirm` (except display-only ones).
452
+
453
+ ### 6.3 `types/objects.ts`
454
+
455
+ Composition objects:
456
+
457
+ - `TextObject<T = "plain_text" | "mrkdwn">` — `{ type, text, emoji?, verbatim? }`
458
+ - `OptionObject` — `{ text, value, description?, url? }`
459
+ - `OptionGroupObject` — `{ label, options[] }`
460
+ - `ConfirmDialogObject` — `{ title, text, confirm, deny, style? }`
461
+ - `DispatchActionConfigObject` — `{ trigger_actions_on?: ("on_enter_pressed" | "on_character_entered")[] }`
462
+ - `FilterObject` — for conversation/channel selects
463
+ - `SlackFileObject` — `{ url?: string; id?: string }`
464
+ - `TriggerObject`, `WorkflowObject` — workflow_button payloads
465
+ - `Style` — `"primary" | "danger" | "confirm"`
466
+
467
+ ### 6.4 `types/rich_text_element.ts`
468
+
469
+ `RichTextBlockElement` union: `text`, `link`, `emoji`, `user`, `usergroup`, `channel`, `broadcast`, `color`, `preformatted`, `rich_text_list`, `rich_text_quote`, `rich_text_section`. Shared `Style = { code?, bold?, italic?, strike? }`.
470
+
471
+ ---
472
+
473
+ ## Chapter 7 — State & render hooks
474
+
475
+ State is kept in a React context defined in `src/store/useGlobalContext.tsx`.
476
+
477
+ ### 7.1 `data` prop (users, channels, usergroups)
478
+
479
+ Passed through to `GlobalProvider`. Used by the mention tokenizers so `<@U123>` can render as `@manish` instead of the raw id.
480
+
481
+ ```ts
482
+ data?: {
483
+ users?: { id: string; name: string }[]
484
+ channels?: { id: string; name: string }[]
485
+ user_groups?: { id: string; name: string }[]
486
+ }
487
+ ```
488
+
489
+ ### 7.2 `hooks` prop (custom renderers)
490
+
491
+ Full shape:
492
+
493
+ ```ts
494
+ type Hooks = {
495
+ user?: (d: { id: string; name: string }) => ReactNode
496
+ channel?: (d: { id: string; name: string }) => ReactNode
497
+ usergroup?: (d: { id: string; name: string }) => ReactNode
498
+ atChannel?: (style?) => ReactNode
499
+ atEveryone?: (style?) => ReactNode
500
+ atHere?: (style?) => ReactNode
501
+ emoji?: (d, parse: () => ReactNode) => ReactNode
502
+ date?: (d) => ReactNode
503
+ link?: (input) => ReactNode // the whole <a> replacement
504
+ }
505
+ ```
506
+
507
+ Useful for wiring mentions to internal routing or using Next.js `<Link>` for URLs.
508
+
509
+ ### 7.3 `useGlobalData` hook
510
+
511
+ Internal hook used by every component that needs access to `data` or `hooks`. Don't re-export; keep private.
512
+
513
+ ---
514
+
515
+ ## Chapter 8 — Rendering pipeline
516
+
517
+ ### 8.1 Dispatcher flow
518
+
519
+ ```
520
+ Message → GlobalProvider → for each block:
521
+ BlockWrapper → getBlockComponent(block) → one of the blocks/*.tsx files
522
+ Blocks that contain elements call getElementComponent(element)
523
+ Text objects go through text_object.tsx (§8.4)
524
+ ```
525
+
526
+ ### 8.2 Markdown parser
527
+
528
+ Folder: `src/utils/markdown_parser/`. Built on `@yozora/parser` with six custom Slack-specific tokenizers so Slack mrkdwn renders correctly.
529
+
530
+ | Syntax | Conversion |
531
+ | --- | --- |
532
+ | `*text*` | → `**text**` (bold) |
533
+ | `_text_` | italic |
534
+ | `~text~` | → `~~text~~` (strikethrough) |
535
+ | `` `code` `` | inline code |
536
+ | `<url\|label>` | → `[label](url)` |
537
+
538
+ Custom tokenizers (each has its own folder with `match.ts`, `parse.ts`, `tokenizer.ts`, `types.ts`):
539
+
540
+ - `slack_user_mention` — `<@U123>` / `<@U123|name>`
541
+ - `slack_channel_mention` — `<#C123>` / `<#C123|name>`
542
+ - `slack_user_group_mention` — `<!subteam^G123>` / `<!subteam^G123|@name>`
543
+ - `slack_broadcast` — `<!channel>`, `<!here>`, `<!everyone>`
544
+ - `slack_date` — `<!date^ts^format|fallback>`
545
+ - `slack_emoji` — `:emoji_name:` including skin-tone variants
546
+
547
+ Output is `ReactNode` via the `elements/` and `sub_elements/` folders (Blockquote, Code, Paragraph + Delete, Emphasis, InlineCode, Link, Strong, Text, HTML, SlackUserMention, etc.).
548
+
549
+ ### 8.3 Emoji parser
550
+
551
+ Folder: `src/utils/emojis/`. `parser.ts` + `list.ts`. Handles `:emoji:` resolution and skin tone modifiers (1–6). Uses `node-emoji` at runtime.
552
+
553
+ ### 8.4 Composition objects
554
+
555
+ Folder: `src/components/composition_objects/`.
556
+
557
+ - `text_object.tsx` — renders `TextObject`. For `mrkdwn` runs through the markdown parser above and applies `hooks.link` to any `<a>`.
558
+ - `confirm_dialog.tsx` — modal overlay for `ConfirmDialogObject`; click-outside cancels; supports `primary` (green) / `danger` (red) styles; full dark mode.
559
+
560
+ ---
561
+
562
+ ## Chapter 9 — Styling
563
+
564
+ ### 9.1 Tailwind scope
565
+
566
+ `tailwind.config.js` sets `important: "#slack_blocks_to_jsx"` so every utility is compiled as `#slack_blocks_to_jsx .class { … !important }`. This guarantees styles win against arbitrary host-app CSS without polluting it.
567
+
568
+ Theme extensions include Slack-accurate grays, blacks, blues, greens, reds, broadcast colors — all with dark-mode variants. Font sizes: `small: 13px`, `base: 15px`, `header: 18px`. Tailwind scans `./src/**/*.{js,ts,jsx,tsx,mdx}`.
569
+
570
+ ### 9.2 Dark mode
571
+
572
+ `darkMode: ["selector", '[data-theme="dark"]']`. Toggled at runtime via the `theme` prop on `Message` which writes `data-theme="dark"` on the outer wrapper.
573
+
574
+ ### 9.3 CSS build pipeline
575
+
576
+ `postcss.config.js` pipeline (in order):
577
+
578
+ 1. `postcss-nesting`
579
+ 2. `tailwindcss`
580
+ 3. `autoprefixer`
581
+ 4. `cssnano`
582
+
583
+ Input: `src/style.css`. Output: `dist/style.css` (~40 KB).
584
+
585
+ ---
586
+
587
+ ## Chapter 10 — Build & distribution
588
+
589
+ ### 10.1 tsup config
590
+
591
+ `tsup.config.ts` compiles `src/index.ts` to CJS + ESM, generates `.d.ts`, minifies, and marks `react-markdown`, `remark-gfm`, `node-emoji` as external.
592
+
593
+ ### 10.2 tsconfig
594
+
595
+ - `target: ES2016`, `module: CommonJS`, `jsx: react-jsx`
596
+ - `strict: true`, `noUncheckedIndexedAccess: true`, `skipLibCheck: true`
597
+ - `noEmit: true` (tsup emits)
598
+
599
+ ### 10.3 Scripts
600
+
601
+ | Script | What it does |
602
+ | --- | --- |
603
+ | `build` | tsup build + `build:css` |
604
+ | `build:css` | PostCSS `src/style.css` → `dist/style.css` |
605
+ | `dev` | tsup watch |
606
+ | `dev:css` | PostCSS watch |
607
+ | `lint` | `tsc --noEmit` |
608
+ | `release` | `pnpm run build && changeset publish` |
609
+ | `release-alpha-without-git` | Publish `--tag alpha --no-git-checks` |
610
+
611
+ ### 10.4 dist/ output
612
+
613
+ | File | Size | Purpose |
614
+ | --- | --- | --- |
615
+ | `index.js` | ~556 KB | CJS bundle (minified) |
616
+ | `index.mjs` | ~552 KB | ESM bundle (minified) |
617
+ | `index.d.ts` / `index.d.mts` | ~82 KB | TS declarations |
618
+ | `style.css` | ~40 KB | Compiled Tailwind bundle |
619
+
620
+ ### 10.5 npm publish hygiene
621
+
622
+ `.npmignore` excludes: `.changeset`, `.github`, `node_modules`, `src`, `.env`, `.prettierrc`, `CHANGELOG.md`, `pnpm-lock.yaml`, `tailwind.config.js`, `tsconfig.json`, `.DS_Store`, `postcss.config.js`.
623
+
624
+ `.npmrc`: `auto-install-peers=true`.
625
+
626
+ `package.json` `exports`: `main` (CJS), `module` (ESM), `types`.
627
+
628
+ ---
629
+
630
+ ## Chapter 11 — Release process
631
+
632
+ From `PUBLISH_GUIDE.md`, in short:
633
+
634
+ 1. Commit source changes.
635
+ 2. `pnpm changeset` — pick patch / minor / major; write summary.
636
+ 3. `git push origin main` — triggers GitHub Actions (`.github/workflows/`).
637
+ 4. CI runs lint + build.
638
+ 5. Changesets bot opens a "Version Packages" PR (bumps version, updates `CHANGELOG.md`).
639
+ 6. Merge that PR → publish workflow runs `pnpm run release` → `pnpm run build && changeset publish`.
640
+
641
+ `RELEASE_NOTES.md` tells the story of v1.0.1 — the "full Block Kit parity" release — after 80+ iterative releases. Major breaking changes from that version:
642
+
643
+ - `react-markdown` + `remark-gfm` externalized (host apps must transpile them in Next.js).
644
+ - Actions block now routes **all** element types; previously silently dropped unsupported ones.
645
+ - Input block accepts all input element types; previously only `plain_text_input` and `checkboxes`.
646
+
647
+ New in 1.0.1: Context Actions, Markdown, Plan, Task Card blocks; 22 new interactive elements; confirm dialogs everywhere; dark mode via `data-theme`; GFM markdown; Slack-hosted files for images; `expand` on section.
648
+
649
+ Current version at time of writing: **1.0.3**.
650
+
651
+ ---
652
+
653
+ ## Chapter 12 — Test fixtures
654
+
655
+ Folder: `test-blocks/`. Used as manual/visual test inputs. Each file is a complete `blocks[]` payload.
656
+
657
+ | File | Covers |
658
+ | --- | --- |
659
+ | `00-all-blocks-part1.json` / `part2.json` | Kitchen-sink of every block type |
660
+ | `01-rich-text-nested.json` | Nested rich text formatting |
661
+ | `02-actions-confirm-dialogs.json` | Actions + confirm modals |
662
+ | `03-all-input-types.json` | All input elements |
663
+ | `04-option-groups-multi-selects.json` | Option groups + multi-selects |
664
+ | `05-markdown-block.json` | GFM markdown (tables, code, task lists) |
665
+ | `06-plan-task-cards.json` | Plan + task card statuses |
666
+ | `07-context-actions-feedback.json` | Feedback buttons |
667
+ | `08-table-image-video.json` | Table + image + video |
668
+ | `09-pr-review-workflow.json` | Workflow buttons |
669
+ | `10-kitchen-sink.json` | Large combined payload |
670
+ | `11-alert-card-carousel.json` | All 5 alert levels, card (hero + icon + actions), standalone card, 3-card pricing carousel |
671
+ | `slack-safe-part1.json` | Compatibility fixture |
672
+
673
+ ---
674
+
675
+ ## Chapter 13 — Complete file tree
676
+
677
+ ```
678
+ slack blocks to jsx library/
679
+ ├── .changeset/
680
+ ├── .github/
681
+ ├── dist/
682
+ │ ├── index.js index.mjs index.d.ts index.d.mts style.css
683
+ ├── src/
684
+ │ ├── index.ts # Public exports
685
+ │ ├── message.tsx # <Message> (entry)
686
+ │ ├── header.tsx # App header subcomponent
687
+ │ ├── block_wrapper.tsx # Per-block wrapper
688
+ │ ├── style.css # Tailwind source
689
+ │ ├── components/
690
+ │ │ ├── index.tsx # getBlockComponent / getElementComponent
691
+ │ │ ├── blocks/
692
+ │ │ │ ├── section.tsx divider.tsx image.tsx context.tsx
693
+ │ │ │ ├── header.tsx actions.tsx input.tsx video.tsx
694
+ │ │ │ ├── file.tsx table.tsx markdown_block.tsx
695
+ │ │ │ ├── plan.tsx task_card.tsx context_actions.tsx
696
+ │ │ │ ├── alert.tsx card.tsx carousel.tsx (new in v1.1.0)
697
+ │ │ │ └── rich_text/
698
+ │ │ │ ├── rich_text.tsx
699
+ │ │ │ ├── rich_text_section.tsx
700
+ │ │ │ ├── rich_text_list_wrapper.tsx
701
+ │ │ │ ├── rich_text_section_color.tsx
702
+ │ │ │ ├── rich_text_section_emoji.tsx
703
+ │ │ │ ├── rich_text_section_link.tsx
704
+ │ │ │ ├── rich_text_section_user.tsx
705
+ │ │ │ ├── rich_text_section_usergroup.tsx
706
+ │ │ │ ├── rich_text_section_channel.tsx
707
+ │ │ │ └── rich_text_section_broadcast.tsx
708
+ │ │ ├── elements/ # 28 *_element.tsx files (see Ch. 5)
709
+ │ │ └── composition_objects/
710
+ │ │ ├── text_object.tsx
711
+ │ │ └── confirm_dialog.tsx
712
+ │ ├── store/
713
+ │ │ └── useGlobalContext.tsx # GlobalProvider + useGlobalData
714
+ │ ├── types/
715
+ │ │ ├── layout.ts elements.ts objects.ts rich_text_element.ts
716
+ │ └── utils/
717
+ │ ├── date.ts
718
+ │ ├── is_accessory_stacked.ts
719
+ │ ├── merge_classes.ts
720
+ │ ├── numbers.ts
721
+ │ ├── sanitize_for_slack.ts
722
+ │ ├── emojis/{parser.ts, list.ts}
723
+ │ └── markdown_parser/
724
+ │ ├── parser.tsx types.ts
725
+ │ ├── elements/{blockquote.tsx, code.tsx, paragraph.tsx}
726
+ │ ├── sub_elements/
727
+ │ │ ├── delete.tsx emphasis.tsx html.tsx inline_code.tsx
728
+ │ │ ├── link.tsx slack_broadcast.tsx slack_channel_mention.tsx
729
+ │ │ ├── slack_date.tsx slack_emoji.tsx
730
+ │ │ ├── slack_user_group_mention.tsx slack_user_mention.tsx
731
+ │ │ ├── strong.tsx text.tsx
732
+ │ └── tokenizers/
733
+ │ ├── slack_user_mention/
734
+ │ ├── slack_channel_mention/
735
+ │ ├── slack_user_group_mention/
736
+ │ ├── slack_broadcast/
737
+ │ ├── slack_date/
738
+ │ └── slack_emoji/
739
+ ├── test-blocks/ # JSON fixtures (see Ch. 12)
740
+ ├── package.json tsconfig.json tsup.config.ts
741
+ ├── tailwind.config.js postcss.config.js .prettierrc
742
+ ├── readme.md PUBLISH_GUIDE.md RELEASE_NOTES.md
743
+ ├── .npmignore .npmrc .env .gitignore
744
+ └── pnpm-lock.yaml
745
+ ```
746
+
747
+ ---
748
+
749
+ ## Chapter 14 — Architectural conventions
750
+
751
+ Rules worth knowing before making changes:
752
+
753
+ 1. **File naming:** snake_case for all files in `src/` (`section.tsx`, `button_element.tsx`).
754
+ 2. **Dispatcher pattern:** never `switch` on `block.type` inside a block component. Always let `components/index.tsx` route. Returning `null` for unsupported types is intentional — it means forward-compat without crashes.
755
+ 3. **Tailwind scope:** every visible component must render inside the `#slack_blocks_to_jsx` wrapper — otherwise `important` selectors don't match.
756
+ 4. **Dark mode:** use Tailwind `dark:` variants. Don't read `theme` prop directly in components; rely on the `data-theme` attribute and CSS.
757
+ 5. **Mentions:** never render raw `<@U123>` in components. Use the markdown parser so tokenizers and `hooks` fire.
758
+ 6. **Hooks over forking:** anything that needs host-app integration (routing, avatars, emoji catalog) should be exposed via the `hooks` prop, not hardcoded.
759
+ 7. **Externalization:** `react-markdown`, `remark-gfm`, `node-emoji` must stay external in tsup config — bundling them again will break Next.js ESM interop.
760
+ 8. **Accessibility:** preserve the `accessibility_label` prop on buttons; use semantic tags (button, a, input) — this mirrors Slack's own a11y posture.
761
+ 9. **Type-first:** every block/element addition requires a matching interface in `src/types/` before the component lands.
762
+ 10. **Changesets:** any user-facing change needs a changeset before merge.
763
+
764
+ ---
765
+
766
+ ## Chapter 15 — Change log of this doc
767
+
768
+ | Date | Author | Note |
769
+ | --- | --- | --- |
770
+ | 2026-04-17 | Claude + Mash | Initial knowledge base created from full source walkthrough at v1.0.3. |
771
+ | 2026-04-17 | Claude + Mash | Added Ch. 0 — How to use this knowledge base (usage, reading strategy, update triggers, update procedure, style rules, ownership). |
772
+ | 2026-04-17 | Claude + Mash | v1.1.0 release prep: added three new blocks (`alert`, `card`, `carousel`) matching Slack's 2026-04-16 Block Kit launch. Ch. 4 block count 14 → 17 (new §4.6 "Status & rich-container blocks"). Ch. 6.1 type list updated with `AlertBlock`, `CardBlock`, `CarouselBlock`, `CardImage`, `AlertLevel`. Ch. 12 gained `11-alert-card-carousel.json` fixture. Ch. 13 file tree updated. Changeset: `.changeset/add-alert-card-carousel-blocks.md`. |