Block Kit is a declarative JSON-based UI system used for sandboxed plugin admin pages. Instead of running plugin JavaScript in the browser, the host (EmDash) renders blocks based on a JSON response from the plugin. This ensures security for runtime-installed plugins.
Lifecycle:
- User navigates to the plugin admin page.
- The admin sends a
page_load interaction to the plugin's admin route. - The plugin returns a
BlockResponse containing an array of blocks. - The admin renders these blocks using a
BlockRenderer. - User interactions (e.g., button clicks, form submissions) trigger new interactions sent back to the plugin.
- The plugin responds with new blocks or updates.
Note: Trusted plugins (declared in astro.config.ts) can bypass this by shipping custom React components. Block Kit is specifically for sandboxed, runtime-installed plugins.
import type { BlockInteraction } from "@emdash-cms/blocks";
routes: {
admin: {
handler: async (ctx) => {
// EmDash parses the request body once and exposes it as ctx.input;
// BlockInteraction is the discriminated union of page_load,
// block_action, and form_submit payloads.
const interaction = ctx.input as BlockInteraction;
if (interaction.type === "page_load") {
return {
blocks: [
{ type: "header", text: "My Plugin Settings" },
{
type: "form",
block_id: "settings",
fields: [
{ type: "text_input", action_id: "api_url", label: "API URL" },
{ type: "toggle", action_id: "enabled", label: "Enabled", initial_value: true },
],
submit: { label: "Save", action_id: "save" },
},
],
};
}
if (interaction.type === "form_submit" && interaction.action_id === "save") {
await ctx.kv.set("settings", interaction.values);
return {
blocks: [/* updated blocks */],
toast: { message: "Settings saved", type: "success" },
};
}
};
},
},
}