Repository: speee/jsx-slack (ts)

Issue:
Container component for App Home surface
Today Slack introduced "App Home", the new Block Kit surface for the interaction between Slack app and user.
https://api.slack.com/reference/app-home

An usage of Block Kit for App Home is not so different from message. User still can compose contents for App Home by using jsx-slack's `<Blocks>` container.

So why should we create container component? Now [the official document of layout blocks](https://api.slack.com/reference/block-kit/blocks) are sorted to show the supported blocks in each surfaces. Providing `<Home>` container component, with check valid blocks strictly (like #64), is better for composing correct blocks suitable with each view.

Pull request:
Add an experimental <Home> container component for home tab
I've added (an experimental) `<Home>` container component to generate view payload for new [home tab](https://api.slack.com/surfaces/tabs).

```jsx
<Home>
  <Image src="https://source.unsplash.com/random/960x240?home" alt="home" />
  <Section>
    <b>Welcome back to my home!</b> :house_with_garden:
  </Section>
  <Divider />
  <Section>What's next?</Section>
  <Actions>
    <Button actionId="tickets">See assigned tickets to me :ticket:</Button>
    <Button actionId="reminder">Remind a task later :memo:</Button>
    <Button actionId="pomodoro">Start pomodoro timer :tomato:</Button>
  </Actions>
</Home>
```

https://deploy-preview-78--speee-jsx-slack.netlify.com/#home

jsx-slack treats `<Home>` as experimental container during Slack's home tab is open beta.

Resolves #75.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8118d81..57ad12d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,13 +2,15 @@
 
 ## [Unreleased]
 
-### Changed
+### Added
 
-- Improve REPL demo to follow Block Kit Builder updates ([#77](https://github.com/speee/jsx-slack/pull/77))
+- Add (an experimental) `<Home>` container component for home tab ([#75](https://github.com/speee/jsx-slack/issues/75), [#78](https://github.com/speee/jsx-slack/pull/78))
+- "Copy to clipboard" button on REPL demo ([#77](https://github.com/speee/jsx-slack/pull/77))
 
 ### Fixed
 
 - Throw an error when using `<File>` in `<Modal>` ([#76](https://github.com/speee/jsx-slack/pull/76))
+- REPL demo can transfer the complete modal JSON to Block Kit Builder ([#77](https://github.com/speee/jsx-slack/pull/77))
 
 ### Deprecated
 
diff --git a/README.md b/README.md
index 2cf56ab..0a5478a 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 [npm]: https://www.npmjs.com/package/@speee-js/jsx-slack
 [license]: ./LICENSE
 
-Build JSON object for [Slack][slack] [Block Kit] from readable [JSX].
+Build JSON object for [Slack][slack] [Block Kit] surfaces from readable [JSX].
 
 [slack]: https://slack.com
 [jsx]: https://reactjs.org/docs/introducing-jsx.html
@@ -25,7 +25,7 @@ Build JSON object for [Slack][slack] [Block Kit] from readable [JSX].
 
 ### Features
 
-- **[Block Kit as components](docs/jsx-components-for-block-kit.md)** - Build your message by Block Kit block components.
+- **[Block Kit as components](docs/jsx-components-for-block-kit.md)** - Build contents for any surfaces by composing components for Block Kit with JSX.
 - **[HTML-like formatting](docs/html-like-formatting.md)** - Keep a readability by using well-known elements.
 
 ## Motivation
@@ -36,9 +36,9 @@ Slack has shipped [Block Kit] and [Block Kit Builder], and efforts to develop ap
 
 ## Project goal
 
-A project goal is creating an interface to build the maintainable Slack message and modals with interactive contents with confidence via readable [JSX].
+A project goal is creating an interface to compose maintainable contents for Slack with confidence via readable [JSX].
 
-jsx-slack would allow composing blocks with predictable HTML-like markup. It helps in understanding the structure of complex messages and interactions.
+jsx-slack would allow composing blocks with predictable HTML-like markup. It helps in understanding the structure of complex contents and interactions.
 
 ## Install
 
@@ -182,6 +182,24 @@ By using jsx-slack, you can build a template with piling up Block Kit blocks by
 </Modal>
 ```
 
+### [For home tab](https://speee-jsx-slack.netlify.com/#home) _(experimental)_
+
+```jsx
+<Home>
+  <Image src="https://source.unsplash.com/random/960x240?home" alt="home" />
+  <Section>
+    <b>Welcome back to my home!</b> :house_with_garden:
+  </Section>
+  <Divider />
+  <Section>What's next?</Section>
+  <Actions>
+    <Button actionId="tickets">See assigned tickets to me :ticket:</Button>
+    <Button actionId="reminder">Remind a task later :memo:</Button>
+    <Button actionId="pomodoro">Start pomodoro timer :tomato:</Button>
+  </Actions>
+</Home>
+```
+
 ### References
 
 - **[JSX components for Block Kit](docs/jsx-components-for-block-kit.md)**
diff --git a/demo/example.js b/demo/example.js
index 10d6af9..630d147 100644
--- a/demo/example.js
+++ b/demo/example.js
@@ -1,4 +1,4 @@
-export const blockKit = `
+export const message = `
 <Blocks>
   <Section>
     <p>
@@ -44,6 +44,22 @@ export const modal = `
 </Modal>
 `.trim()
 
+export const home = `
+<Home>
+  <Image src="https://source.unsplash.com/random/960x240?home" alt="home" />
+  <Section>
+    <b>Welcome back to my home!</b> :house_with_garden:
+  </Section>
+  <Divider />
+  <Section>What's next?</Section>
+  <Actions>
+    <Button actionId="tickets">See assigned tickets to me :ticket:</Button>
+    <Button actionId="reminder">Remind a task later :memo:</Button>
+    <Button actionId="pomodoro">Start pomodoro timer :tomato:</Button>
+  </Actions>
+</Home>
+`.trim()
+
 export const dialog = `
 <!-- ⚠️ Please notice that dialog components were deprecated. -->
 <Dialog callbackId="createUser" title="Create user">
diff --git a/demo/index.html b/demo/index.html
index 80b99e1..a967e53 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -56,8 +56,9 @@
           <small>(Try editting)</small>
           <select id="examples">
             <option value="" selected>Choose examples...</option>
-            <option value="blockKit">Block Kit messaging</option>
-            <option value="modal">Block Kit modal</option>
+            <option value="message">Message</option>
+            <option value="modal">Modal</option>
+            <option value="home">App Home</option>
           </select>
         </h2>
         <div id="jsx"></div>
diff --git a/demo/index.js b/demo/index.js
index 0b1170d..33f4acb 100644
--- a/demo/index.js
+++ b/demo/index.js
@@ -80,7 +80,7 @@ const jsxEditor = CodeMirror(jsx, {
       return examples[hash]
     }
 
-    return examples.blockKit
+    return examples.message
   })(),
 })
 
@@ -115,6 +115,8 @@ const convert = () => {
       setPreview({ blocks: JSON.stringify(output), mode: 'message' })
     } else if (output.type === 'modal') {
       setPreview({ view: JSON.stringify(output), mode: 'modal' })
+    } else if (output.type === 'home') {
+      setPreview({ view: JSON.stringify(output), mode: 'appHome' })
     } else {
       setPreview(false)
     }
diff --git a/demo/schema.js b/demo/schema.js
index 2e5dd35..46195c2 100644
--- a/demo/schema.js
+++ b/demo/schema.js
@@ -68,7 +68,7 @@ const commonKnownBlocks = [
 ]
 
 const schema = {
-  '!top': ['Blocks', 'Modal'],
+  '!top': ['Blocks', 'Modal', 'Home'],
 
   // Container components
   Blocks: {
@@ -103,6 +103,15 @@ const schema = {
     ],
   },
 
+  Home: {
+    attrs: {
+      privateMetadata: null,
+      callbackId: null,
+      externalId: null,
+    },
+    children: commonKnownBlocks,
+  },
+
   // Block Kit components
   Section: {
     attrs: blockCommonAttrs,
diff --git a/docs/block-containers.md b/docs/block-containers.md
index 00cb7fa..9a88a6c 100644
--- a/docs/block-containers.md
+++ b/docs/block-containers.md
@@ -2,13 +2,13 @@
 
 # Block containers
 
-[Slack provides multiple surfaces](https://api.slack.com/block-kit/surfaces) to place Block Kit [layout blocks](layout-blocks.md). So you should choose the parent container component depending on purpose.
+[Slack provides multiple surfaces](https://api.slack.com/surfaces) to place Block Kit [layout blocks](layout-blocks.md). So you should choose the parent container component depending on purpose.
 
-## <a name="blocks" id="blocks"></a> `<Blocks>`: The basic container for blocks
+## <a name="blocks" id="blocks"></a> [`<Blocks>`: The basic container for messages](https://api.slack.com/surfaces/messages)
 
-A basic container component for Block Kit suited to [messaging](https://api.slack.com/block-kit/surfaces/messages). Wrap layout block components in `<Blocks>`.
+A basic container component for Block Kit suited to [messages](https://api.slack.com/surfaces/messages). Wrap layout block components in `<Blocks>`.
 
-When composing message for using in API such as [`chat.postMessage`](https://api.slack.com/methods/chat.postMessage), you should pass generated array by this container component to `blocks` field in payloads.
+When composing message for using in API such as [`chat.postMessage`](https://api.slack.com/methods/chat.postMessage), you should pass generated array by `<Blocks>` container component to `blocks` field in payloads.
 
 ```javascript
 import { WebClient } from '@slack/client'
@@ -26,15 +26,15 @@ api.chat.postMessage({
 })
 ```
 
-## <a name="modal" id="modal"></a> `<Modal>`: The modal view container
+## <a name="modal" id="modal"></a> [`<Modal>`: The view container for modals](https://api.slack.com/surfaces/modals)
 
-A container component for [Block Kit in modals](https://api.slack.com/block-kit/surfaces/modals). You can create JSON object for modal powered by Block Kit.
+The container component for [modals](https://api.slack.com/block-kit/surfaces/modals). You can build view payload for modal through JSX.
 
 A generated object by `<Modal>` container should pass to a `view` field in [`views.*`](https://api.slack.com/methods/views.open) API payloads.
 
 ```javascript
 api.views.open({
-  // NOTE: trigger_id received from another interaction is required.
+  // NOTE: trigger_id received from interaction is required.
   trigger_id: 'xxxxx.xxxxx.xxxxxxxxxxxx',
   view: JSXSlack(
     <Modal title="My first modal">
@@ -66,7 +66,7 @@ export default function shareModal(opts) {
 }
 ```
 
-[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22image%22%2C%22alt_text%22%3A%22image%22%2C%22image_url%22%3A%22https%3A%2F%2Fsource.unsplash.com%2Frandom%2F1200x400%22%7D%2C%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Subject%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22subject%22%7D%7D%2C%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Comment%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Atrue%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22comment%22%2C%22multiline%22%3Atrue%2C%22max_length%22%3A500%7D%7D%2C%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Share%20with...%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22conversations_select%22%2C%22action_id%22%3A%22shareWith%22%7D%7D%5D&mode=modal)
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?mode=modal&view=%7B%22type%22%3A%22modal%22%2C%22title%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Share%22%2C%22emoji%22%3Atrue%7D%2C%22submit%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Share%22%2C%22emoji%22%3Atrue%7D%2C%22close%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Cancel%22%2C%22emoji%22%3Atrue%7D%2C%22blocks%22%3A%5B%7B%22type%22%3A%22image%22%2C%22alt_text%22%3A%22image%22%2C%22image_url%22%3A%22https%3A%2F%2Fsource.unsplash.com%2Frandom%2F1200x400%22%7D%2C%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Subject%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22subject%22%7D%7D%2C%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Comment%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Atrue%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22comment%22%2C%22multiline%22%3Atrue%2C%22max_length%22%3A500%7D%7D%2C%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Share%20with...%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22conversations_select%22%2C%22action_id%22%3A%22shareWith%22%7D%7D%5D%7D)
 
 ### Props
 
@@ -81,6 +81,33 @@ export default function shareModal(opts) {
 
 > :information_source: Slack requires the submit text when modal has component for inputs, so jsx-slack would set the text "Submit" as the default value of `submit` prop if you are setting no submit text in any way together with using input components.
 
+## <a name="home" id="home"></a> [`<Home>`: The view container for home tabs](https://api.slack.com/surfaces/tabs) _(experimental)_
+
+The container component for [home tabs](https://api.slack.com/surfaces/tabs). You can build view payload for home tab.
+
+A generated object by `<Home>` container should pass to a view field in [`views.publish`](https://api.slack.com/methods/views.publish) API payloads.
+
+```javascript
+api.views.publish({
+  user_id: 'UXXXXXXXX',
+  view: JSXSlack(
+    <Home>
+      <Section>Welcome to my home!</Section>
+    </Home>
+  ),
+})
+```
+
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?mode=appHome&view=%7B%22type%22%3A%22home%22%2C%22blocks%22%3A%5B%7B%22type%22%3A%22section%22%2C%22text%22%3A%7B%22text%22%3A%22Welcome%20to%20my%20home!%22%2C%22type%22%3A%22mrkdwn%22%2C%22verbatim%22%3Atrue%7D%7D%5D%7D)
+
+> :warning: _`<Home>` container component is experimental._ Slack's home tab is currently open beta, so we expect its spec might be updated with high-frequency.
+
+### Props
+
+- `privateMetadata` (optional): An optional string that can be found in payloads of some interactive events Slack app received. (3000 characters maximum)
+- `callbackId` (optional): An identifier for this modal to recognize it in various events. (255 characters maximum)
+- `externalId` (optional): A unique ID for all views on a per-team basis.
+
 ---
 
 ###### [Top](../README.md) &raquo; [JSX components for Block Kit](jsx-components-for-block-kit.md) &raquo; Block containers
diff --git a/docs/block-elements.md b/docs/block-elements.md
index e8e3269..d62f9c2 100644
--- a/docs/block-elements.md
+++ b/docs/block-elements.md
@@ -98,7 +98,7 @@ By defining `multiple` attribute, you also can provide [the selectable menu from
 In `<Modal>` container, select-like components will work as [the input component for modal](#input-components-for-modal) by passing suitable props such as required `label` prop. Thereby it would allow natural templating like as HTML form.
 
 ```jsx
-<Modal title="My App">
+<Modal title="Programming survey">
   <Select
     label="Language"
     name="language"
@@ -114,14 +114,14 @@ In `<Modal>` container, select-like components will work as [the input component
 </Modal>
 ```
 
-[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22input%22%2C%22hint%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Pick%20language%20you%20want%20to%20learn.%22%2C%22emoji%22%3Atrue%7D%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Language%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22static_select%22%2C%22action_id%22%3A%22language%22%2C%22options%22%3A%5B%7B%22value%22%3A%22javascript%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22JavaScript%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22python%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Python%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22java%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Java%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22c-sharp%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22C%23%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22php%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22PHP%22%2C%22emoji%22%3Atrue%7D%7D%5D%7D%7D%5D&mode=modal)
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?mode=modal&view=%7B%22type%22%3A%22modal%22%2C%22title%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Programming%20survey%22%2C%22emoji%22%3Atrue%7D%2C%22submit%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit%22%2C%22emoji%22%3Atrue%7D%2C%22blocks%22%3A%5B%7B%22type%22%3A%22input%22%2C%22hint%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Pick%20language%20you%20want%20to%20learn.%22%2C%22emoji%22%3Atrue%7D%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Language%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22static_select%22%2C%22action_id%22%3A%22language%22%2C%22options%22%3A%5B%7B%22value%22%3A%22javascript%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22JavaScript%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22python%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Python%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22java%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Java%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22c-sharp%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22C%23%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22value%22%3A%22php%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22PHP%22%2C%22emoji%22%3Atrue%7D%7D%5D%7D%7D%5D%7D)
 
 The above JSX means exactly same as following usage of [`<Input>` layout block](layout-blocks.md#input):
 
 <!-- prettier-ignore-start -->
 
 ```jsx
-<Modal title="My App">
+<Modal title="Programming survey">
   <Input label="Language" title="Pick language you want to learn." required>
     <Select actionId="language">
       ...
@@ -370,7 +370,7 @@ An easy way to let the user selecting any date is using `<DatePicker>` component
 </Modal>
 ```
 
-[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Date%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Atrue%2C%22element%22%3A%7B%22type%22%3A%22datepicker%22%2C%22action_id%22%3A%22date%22%7D%7D%5D&mode=modal)
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?mode=modal&view=%7B%22type%22%3A%22modal%22%2C%22title%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22My%20App%22%2C%22emoji%22%3Atrue%7D%2C%22submit%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit%22%2C%22emoji%22%3Atrue%7D%2C%22close%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Cancel%22%2C%22emoji%22%3Atrue%7D%2C%22blocks%22%3A%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Date%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Atrue%2C%22element%22%3A%7B%22type%22%3A%22datepicker%22%2C%22action_id%22%3A%22date%22%7D%7D%5D%7D)
 
 ##### Props for modal's input
 
@@ -446,7 +446,7 @@ It has an interface similar to `<input>` HTML element and `<input>` intrinsic HT
 </Modal>
 ```
 
-[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Title%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22title%22%2C%22max_length%22%3A80%7D%7D%5D&mode=modal)
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?mode=modal&view=%7B%22type%22%3A%22modal%22%2C%22title%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22My%20App%22%2C%22emoji%22%3Atrue%7D%2C%22submit%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit%22%2C%22emoji%22%3Atrue%7D%2C%22close%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Cancel%22%2C%22emoji%22%3Atrue%7D%2C%22blocks%22%3A%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Title%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22title%22%2C%22max_length%22%3A80%7D%7D%5D%7D)
 
 #### <a name="input-props" id="input-props"></a> Props
 
@@ -540,7 +540,7 @@ As same as `<Input type="hidden">`, `submit` prop in `<Modal>` must not define w
 </Modal>
 ```
 
-[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Tweet%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22tweet%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22What%E2%80%99s%20happening%3F%22%2C%22emoji%22%3Afalse%7D%2C%22multiline%22%3Atrue%2C%22max_length%22%3A280%7D%7D%5D&mode=modal)
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?mode=modal&view=%7B%22type%22%3A%22modal%22%2C%22title%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22My%20App%22%2C%22emoji%22%3Atrue%7D%2C%22submit%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit%22%2C%22emoji%22%3Atrue%7D%2C%22close%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Cancel%22%2C%22emoji%22%3Atrue%7D%2C%22blocks%22%3A%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Tweet%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22tweet%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22What%E2%80%99s%20happening%3F%22%2C%22emoji%22%3Afalse%7D%2C%22multiline%22%3Atrue%2C%22max_length%22%3A280%7D%7D%5D%7D)
 
 `<textarea>` intrinsic HTML element also works as well.
 
diff --git a/docs/jsx-components-for-block-kit.md b/docs/jsx-components-for-block-kit.md
index 25879fa..9735dec 100644
--- a/docs/jsx-components-for-block-kit.md
+++ b/docs/jsx-components-for-block-kit.md
@@ -4,8 +4,9 @@
 
 ## **[Block containers](block-containers.md)**
 
-- [**`<Blocks>`**: The basic container for blocks](block-containers.md#blocks)
-- [**`<Modal>`**: The modal view container](block-containers.md#modal)
+- [**`<Blocks>`**: The basic container for messages](block-containers.md#blocks)
+- [**`<Modal>`**: The view container for modals](block-containers.md#modal)
+- [**`<Home>`**: The view container for home tabs](block-containers.md#home) _(experimental)_
 
 ## **[Layout blocks](layout-blocks.md)**
 
diff --git a/docs/layout-blocks.md b/docs/layout-blocks.md
index e84a0f3..8f725fb 100644
--- a/docs/layout-blocks.md
+++ b/docs/layout-blocks.md
@@ -193,14 +193,14 @@ Display one of interactive components for input to collect information from user
 If you want to use `<Input>` as layout block, you have to place one of [available interactive components](#available-interactive-components) as a child.
 
 ```jsx
-<Modal title="My App">
+<Modal title="Register" submit="OK" close="Cancel">
   <Input label="User" title="Please select one of users." required>
     <UsersSelect placeholder="Choose user..." />
   </Input>
 </Modal>
 ```
 
-[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22input%22%2C%22hint%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Please%20select%20one%20of%20users.%22%2C%22emoji%22%3Atrue%7D%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22User%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Atrue%2C%22element%22%3A%7B%22type%22%3A%22users_select%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Choose%20user...%22%2C%22emoji%22%3Atrue%7D%7D%7D%5D&mode=modal)
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?mode=modal&view=%7B%22type%22%3A%22modal%22%2C%22title%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Register%22%2C%22emoji%22%3Atrue%7D%2C%22submit%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22OK%22%2C%22emoji%22%3Atrue%7D%2C%22close%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Cancel%22%2C%22emoji%22%3Atrue%7D%2C%22blocks%22%3A%5B%7B%22type%22%3A%22input%22%2C%22hint%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Please%20select%20one%20of%20users.%22%2C%22emoji%22%3Atrue%7D%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22User%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22users_select%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Choose%20user...%22%2C%22emoji%22%3Atrue%7D%7D%7D%5D%7D)
 
 ### Props
 
diff --git a/package.json b/package.json
index 781f961..48f068c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@speee-js/jsx-slack",
   "version": "0.10.2",
-  "description": "Build JSON object for Slack Block Kit from readable JSX",
+  "description": "Build JSON object for Slack Block Kit surfaces from readable JSX",
   "author": {
     "name": "Yuki Hattori",
     "url": "https://github.com/yhatt"
@@ -16,7 +16,8 @@
     "block-kit",
     "jsx",
     "notification",
-    "bot"
+    "bot",
+    "modal"
   ],
   "homepage": "https://github.com/speee/jsx-slack#readme",
   "bugs": {
diff --git a/src/block-kit/Blocks.tsx b/src/block-kit/Blocks.tsx
index 475b21b..e8d439b 100644
--- a/src/block-kit/Blocks.tsx
+++ b/src/block-kit/Blocks.tsx
@@ -14,7 +14,8 @@ export interface BlockComponentProps {
 }
 
 export enum InternalBlockType {
-  modal = 'modal',
+  Modal = 'modal',
+  Home = 'home',
 }
 
 export const blockTypeSymbol = Symbol('jsx-slack-block-type')
@@ -30,13 +31,16 @@ const basicBlocks = ['actions', 'context', 'divider', 'image', 'section']
 knownBlocks.set(undefined, [...basicBlocks, 'file'])
 
 // Modal
-knownBlocks.set(InternalBlockType.modal, [
+knownBlocks.set(InternalBlockType.Modal, [
   ...basicBlocks,
   'input',
   internalHiddenType,
   internalSubmitType,
 ])
 
+// Home
+knownBlocks.set(InternalBlockType.Home, basicBlocks)
+
 export const Blocks: JSXSlack.FC<BlocksProps> = props => {
   const internalType: InternalBlockType | undefined = props[blockTypeSymbol]
 
diff --git a/src/block-kit/Home.tsx b/src/block-kit/Home.tsx
new file mode 100644
index 0000000..17c8c12
--- /dev/null
+++ b/src/block-kit/Home.tsx
@@ -0,0 +1,35 @@
+/** @jsx JSXSlack.h */
+import { JSXSlack } from '../jsx'
+import { ObjectOutput } from '../utils'
+import {
+  BlockComponentProps,
+  BlocksInternal,
+  blockTypeSymbol,
+  InternalBlockType,
+} from './Blocks'
+
+export interface HomeProps {
+  callbackId?: string
+  children: JSXSlack.Children<BlockComponentProps>
+  externalId?: string
+  privateMetadata?: string
+}
+
+/** [experimental] */
+export const Home: JSXSlack.FC<HomeProps> = props => {
+  // TODO: Use type definition to validate output
+  return (
+    <ObjectOutput
+      type="home"
+      callback_id={props.callbackId}
+      external_id={props.externalId}
+      private_metadata={props.privateMetadata}
+      blocks={JSXSlack(
+        <BlocksInternal
+          {...{ [blockTypeSymbol]: InternalBlockType.Home }}
+          children={props.children}
+        />
+      )}
+    />
+  )
+}
diff --git a/src/block-kit/Modal.tsx b/src/block-kit/Modal.tsx
index 25bc762..20ba18a 100644
--- a/src/block-kit/Modal.tsx
+++ b/src/block-kit/Modal.tsx
@@ -35,7 +35,7 @@ export const Modal: JSXSlack.FC<ModalProps> = props => {
 
   const blocks = JSXSlack(
     <BlocksInternal
-      {...{ [blockTypeSymbol]: InternalBlockType.modal }}
+      {...{ [blockTypeSymbol]: InternalBlockType.Modal }}
       children={props.children}
     />
   ).reduce((arr, block) => {
diff --git a/src/block-kit/index.ts b/src/block-kit/index.ts
index f70a3ef..4c4cd2e 100644
--- a/src/block-kit/index.ts
+++ b/src/block-kit/index.ts
@@ -1,6 +1,7 @@
 // Containers for Block Kit
 export { Blocks } from './Blocks'
 export { Modal } from './Modal'
+export { Home } from './Home'
 
 // Block Kit blocks
 export { Actions } from './Actions'


Test patch:
diff --git a/test/block-kit/container-components.tsx b/test/block-kit/container-components.tsx
index bd591be..7e85ef2 100644
--- a/test/block-kit/container-components.tsx
+++ b/test/block-kit/container-components.tsx
@@ -4,6 +4,7 @@ import JSXSlack, {
   Blocks,
   Escape,
   File,
+  Home,
   Input,
   Modal,
   Option,
@@ -68,15 +69,13 @@ describe('Container components', () => {
         type: 'modal',
         title: expect.any(Object),
         blocks: expect.any(Array),
+        callback_id: 'callback_id',
+        external_id: 'external_id',
         submit: { type: 'plain_text', text: 'Submit', emoji: true },
         close: { type: 'plain_text', text: 'Close', emoji: true },
         private_metadata: 'private_metadata',
         clear_on_close: true,
         notify_on_close: false,
-
-        // Fields for API
-        callback_id: 'callback_id',
-        external_id: 'external_id',
       }
 
       expect(
@@ -108,4 +107,72 @@ describe('Container components', () => {
       ).toThrow()
     })
   })
+
+  describe('<Home>', () => {
+    it('generates view payload JSON', () => {
+      const view = {
+        type: 'home',
+        blocks: [{ type: 'section', text: expect.any(Object) }],
+      }
+
+      expect(
+        JSXSlack(
+          <Home>
+            <Section>Hello!</Section>
+          </Home>
+        )
+      ).toStrictEqual(view)
+
+      const viewWithOptions = {
+        type: 'home',
+        callback_id: 'callback_id',
+        external_id: 'external_id',
+        private_metadata: 'private_metadata',
+        blocks: [{ type: 'section', text: expect.any(Object) }],
+      }
+
+      expect(
+        JSXSlack(
+          <Home
+            callbackId="callback_id"
+            externalId="external_id"
+            privateMetadata="private_metadata"
+          >
+            <Section>Hello!</Section>
+          </Home>
+        )
+      ).toStrictEqual(viewWithOptions)
+    })
+  })
+
+  it('throws error when <Modal> has unexpected element', () => {
+    expect(() => JSXSlack(<Home>unexpected</Home>)).toThrow()
+    expect(() =>
+      JSXSlack(
+        <Home>
+          <b>unexpected</b>
+        </Home>
+      )
+    ).toThrow()
+
+    expect(() =>
+      JSXSlack(
+        <Home>
+          <File externalId="external_id" />
+        </Home>
+      )
+    ).toThrow()
+
+    expect(() =>
+      JSXSlack(
+        <Home>
+          <Input label="Select">
+            <Select>
+              <Option value="test">test</Option>
+            </Select>
+          </Input>
+        </Home>
+      )
+    ).toThrow()
+  })
 })