Hooks
Read the provider's state from your own components, and build custom surfaces.
You need these hooks only when you write your own custom blocks or surfaces. The built-in components use them internally.
useBlockKit
Returns the nearest provider’s context: the current surface, input state, resolvers, emoji, errors, timeZone and views, plus the functions elements use to report changes.
import { useBlockKit } from "@nkootstra/block-kit";
function Counter({ blockId }: { blockId: string }) {
const { state } = useBlockKit();
const filled = Object.keys(state[blockId] ?? {}).length;
return <span>{filled} fields filled</span>;
}
Without a provider, it returns defaults: the message surface, empty state, and handlers that do nothing.
Two functions matter for custom elements:
setValue?(blockId: string, actionId: string, value: ElementState | undefined) => void
Records an input's current value in `state`, as Slack does for `view.state.values`. `value` has a `type` plus the element's value fields, for example `{ type: "plain_text_input", value: "hi" }`.
(blockId: string, actionId: string, value: ElementState | undefined) => voiddispatch?(action) => void
Reports an action to `onAction` and `onPayload`. Pass `type`, `action_id` and `block_id`, plus fields such as `value`; `action_ts` is filled in for you.
(action) => voiduseLiveView
const { view, update } = useLiveView(viewProp);
Returns the view a surface should show right now: its view prop, or the content an action last sent with views.update or views.publish. Passing a view prop with new content takes over again. <Modal> and <HomeTab> use it; call it in your own surface to get the same behaviour.
SurfaceScope
Ties the elements below it to one message or view. Their actions carry that container, and message for a message, even when several surfaces share one provider. It also sets surface for them: modal or home for a view container, message otherwise.
import { Blocks, SurfaceScope } from "@nkootstra/block-kit";
<SurfaceScope
container={{ type: "message", messageTs: "1700000000.000100", channelId: "C0RELEASES" }}
>
<div className="sbk-root">
<Blocks blocks={blocks} />
</div>
</SurfaceScope>;
containerContainer
`{ type: "message", messageTs, channelId?, isEphemeral?, message? }` or `{ type: "view", view }`. Becomes the `container` of `block_actions` payloads.
Containermessage?MessageApi
What `onAction` receives as `context.message` for actions in this scope.
MessageApierrors?Record<string, string>
Validation errors by `block_id`, shown on top of the provider's.
Record<string, string>childrenReactNode
ReactNodeuseContainer
useContainer(container);
Registers the message or view a surface renders, so the provider can build full payloads for actions dispatched without a scope. <SurfaceScope> calls it for you; call it yourself only if you dispatch actions outside any scope.