Explore how adaptive content transforms your docs into a dynamic, tailored experience for every user.
Read the docs
LogoLogo
ProductPricingLog inSign up
  • Documentation
  • Developers
  • Guides
  • Changelog
  • Help Center
  • Getting Started
    • Developer Documentation
    • Quickstart
    • Development
    • Publishing
  • Integrations
    • Introduction
    • Using the CLI
    • Configuration
    • ContentKit
      • Component reference
    • Integration runtime
  • Client library
  • Guides
    • Creating a custom unfurl action
    • Creating interactive blocks
    • Referencing your integration in Markdown
    • Working with HTTP requests
    • Using the CLI in CI/CD
  • GitBook API
    • Introduction
    • Authentication
    • API reference
      • Organizations
        • Organization members
        • Organization invites
        • Organization AI ask
      • Docs sites
        • Site share links
        • Site structure
        • Site auth
        • Site preview
        • Site customization
        • Site spaces
        • Site sections
        • Site section groups
        • Site redirects
        • Site MCP servers
        • Site ads
        • Site users
        • Site insights
        • Site AI ask
      • Collections
        • Collection users
        • Collection teams
      • Spaces
        • Space content
        • Space comments
        • Space embeds
        • Space users
        • Space teams
        • Space integrations
        • Git
      • Change requests
        • Change request content
        • Change request contributors
        • Change request reviewers
        • Change request comments
      • Translations
        • Glossary
      • Integrations
      • URLs
      • OpenAPI
        • OpenAPI spec versions
      • Conversations
      • Custom fonts
      • Subdomains
      • Users
      • Teams
        • Team members
      • SSO
      • Storage
      • Custom hostnames
      • System info
    • Rate limiting
    • Pagination
    • Errors
  • Marketplace
    • Overview
    • Submit your app for review
  • Resources
    • Concepts
    • Changelog
    • ContentKit playground
    • GitHub examples
Powered by GitBook
On this page
Edit on GitHub
  1. Guides

Creating interactive blocks

Last updated 4 months ago

Was this helpful?

LogoLogo

Resources

  • Showcase
  • Enterprise
  • Status

Company

  • Careers
  • Blog
  • Community

Policies

  • Subprocessors
  • Terms of Service
CtrlK
  • Buttons and actions
  • Text and other inputs
  • Dynamic binding
  • Editable blocks
  • Webframes and actions
  • Modals
  • Opening urls

Was this helpful?

ContentKit components can be interactive, meaning visitors using your integration can do things like type or click in your components. Different components expose different action handlers, like buttons exposing an onPress event.

When creating your component through the createComponent call, you can also specify how to handle each action through the action prop.

Buttons and actions

The most common interactive elements are buttons. Buttons can be use to trigger an asynchronous action.

<button
    label="Click me"
    onPress={{
        action: 'update-state',
        anotherProperty: 'something'
    }}
/>

When the user presses the button, the action is dispatched to the integration and can be handled in the action callback:

const helloWorldBlock = createComponent({
    ...
    async action(previous, action) {
        switch (action.action) {
            case 'update-state':
                return { state: { content: action.anotherProperty } };
            default:
        }
    },
    ...
});

Text and other inputs

Collecting user input can be done through through textinput. For example, considering the following element:

<textinput state="content" />

When an action is dispatched (ex: when pressing a button in the example above), the value of the input will be accessible as state.content.

Dynamic binding

Interactions with actions are asynchronous, meaning that pressing a button will cause the integration's code to run and re-render the component. But in some cases, there is a need for synchronous binding between the elements to provide a top class user experience (ex: live preview when typing).

ContentKit provides a solution with dynamic binding, connecting multiple elements to a dynamic state.

For example, we can update a webframe by binding directly to a text input:

createComponent({
    componentId: 'demo',
    initialState: {
        content: ''
    },
    async render(element) {
        return (
            <block>
                <hstack>
                    <textinput state="content" />
                    <divider />
                    <webframe
                        source={{ uri: '/iframe.html' }}
                        data={{
                            content: element.dynamicState('content')
                        }}
                        />
                </hstack>
            </block>
        )
    }
})

In the iframe.html, you can handle incoming events by listening to the message event coming from the parent window:

window.addEventListener("message", (event) => {
    if (event.data) {
        const content = event.data.state.content;
    }
});

Editable blocks

Some blocks might be static or only generated from link unfurling, but most blocks are designed to be editable by the user. Editable means that the user can interact with the blocks to change its properties.

Updating the properties of a block is done through a @editor.node.updateProps action:

<block>
    <textinput state="content" />
    <button
        label="Edit"
        onPress={{
            action: '@editor.node.updateProps',
            props: {
                content: element.dynamicState('content')
            }
        }}
        />
</block>

Webframes and actions

Webframes are powerful elements to integrate in GitBook external applications or complete UI. Passing data to the webframe can be done using the data prop. But the webframe also needs to be able to communicate data back to the top component. It can be achieved using the window.postMessage:

window.parent.postMessage({
    action: {
        type: 'doSomething',
    }
}, '*');

Modals

Components can open overlay modals to show extra information or prompt the user. Opening a modal is done by dispatching the @ui.modal.open action:

const block = createComponent({
    componentId: 'block',
    async render(element) {
        return (
            <block>
                <button
                    label="Open modal"
                    onPress={{
                        action: '@ui.modal.open',
                        componentId: 'custommodal',
                        props: {
                            message: 'Hello world'
                        }
                    }}
                />
            </block>
        )
    }
});

Opening the modal will start rendering the component custommodal with the defined props:

const custommodal = createComponent({
    componentId: 'custommodal',
    async render(element) {
        return (
            <modal title="Hello world">
                <button
                    label="Close the modal"
                    onPress={{
                        action: '@ui.modal.close',
                        returnValue: {}
                    }}
                />
            </modal>
        )
    }
});

When closing a modal, data can be returned to the parent component using returnValue. These data will be accessible in the parent component's action handler.

Opening urls

A common pattern is to open a url as a webpage. A default action exists for this:

<button
    onPress={{
        action: '@ui.url.open',
        url: 'https://www.gitbook.com'
    }}
/>