Plugin SDK > Sidebars and sidebar panels

    Sidebars and sidebar panels

    Within the form for editing a record, it can be convenient to have some contextual information to keep an eye on while you are writing, without having to keep several tabs open or interrupt the flow.

    Through plugins it is possible to either add a series of additional and fully customizable sidebars next to your record:

    Or add panels to the default "Info" sidebar:

    You can take a look at a real-world example of both sidebars and sidebar panels in the Web Previews plugin.

    Inside sidebars and sidebar panels, the plugin developer can render what they prefer, while also having the possibility to:

    • access a series of information relating to the record that's being edited, the project in which the plugin is installed or the logged-in user;

    • make calls to DatoCMS to produce various effects and interact with the main application (changing form values, navigating to other pages, triggering notifications, opening modals, etc.);

    Implementing a simple Sidebar Panel

    Let's say we want to create a sidebar panel that will show a link pointing to the website page related to the record we're editing. The first step is to implement the itemFormSidebarPanels hook, to declare our intent to add the panel to the sidebar:

    import { connect, IntentCtx } from 'datocms-plugin-sdk';
    connect({
    itemFormSidebarPanels(model: ItemType, ctx: IntentCtx) {
    return [
    {
    id: 'goToWebsite',
    label: 'Go to website',
    startOpen: true,
    },
    ];
    },
    });

    The code above will add a panel to every record in our project... but maybe not every record in DatoCMS has a specific page in the final website, right?

    It might be better to add some settings to our plugin to let the final user declare the set of models that have permalinks, and the relative URL structure enforced on the frontend:

    itemFormSidebarPanels(model: ItemType, ctx: IntentCtx) {
    const { permalinksByModel } = ctx.plugin.attributes.parameters;
    // Assuming we're saving user preferences in this format:
    // {
    // 'blog_post': '/blog/:slug',
    // 'author': '/author/:slug',
    // ...
    // }
    }
    if (!permalinksByModel[model.attributes.api_key]) {
    // Don't add the panel!
    return [];
    }
    // Add the panel!
    }

    Rendering the panel

    The final step is to actually render the panel itself by implementing the renderItemFormSidebarPanel hook.

    Inside of this hook we initialize React and render a custom component called GoToWebsiteItemFormSidebarPanel, passing down as a prop the second ctx argument, which provides a series of information and methods for interacting with the main application:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { connect, RenderItemFormSidebarPanelCtx } from 'datocms-plugin-sdk';
    connect({
    renderItemFormSidebarPanel(
    sidebarPanelId,
    ctx: RenderItemFormSidebarPanelCtx,
    ) {
    ReactDOM.render(
    <React.StrictMode>
    <GoToWebsiteItemFormSidebarPanel ctx={ctx} />
    </React.StrictMode>,
    document.getElementById('root'),
    );
    },
    });

    A plugin might render different panels, so we can use the sidebarPanelId argument to know which one we are requested to render, and write a specific React component for each of them.

    import { Canvas } from 'datocms-react-ui';
    type PropTypes = {
    ctx: RenderItemFormSidebarPanelCtx;
    };
    function GoToWebsiteItemFormSidebarPanel({ ctx }: PropTypes) {
    return (
    <Canvas ctx={ctx}>
    Hello from the sidebar!
    </Canvas>
    );
    }
    Always use the canvas!

    It is important to wrap the content inside the Canvas component, so that the iframe will continuously auto-adjust its size based on the content we're rendering, and to give our app the look and feel of the DatoCMS web app.

    All we need to do now is to actually render the link to the website, reading from ctx.formValues the slug value and generating the final frontend URL:

    import { ButtonLink } from 'datocms-react-ui';
    function GoToWebsiteItemFormSidebarPanel({ ctx }: PropTypes) {
    if (ctx.itemStatus === 'new') {
    // we're in a record that still has not been persisted
    return <div>Please save the record first!</div>;
    }
    const { permalinksByModel } = ctx.plugin.attributes.parameters;
    const permalinkStructure = permalinksByModel[ctx.itemType.attributes.api_key];
    const url = permalinkStructure.replace(':slug', ctx.formValues.slug);
    return (
    <Canvas ctx={ctx}>
    <ButtonLink href={url} fullWidth>
    View it on the website!
    </ButtonLink>
    </Canvas>
    );
    }

    Implementing a simple custom Sidebar

    Suppose that instead of presenting a link to a webpage, we want to embed the actual web page alongside the record. To do that we need more space than what a sidebar panel can offer, so creating a completely separate sidebar is more appropriate.

    Managing sidebars is very similar to what we just did with sidebar panels. The main difference is in the way you define them. To declare our intent to add the sidebar, implement the itemFormSidebars hook:

    import { connect, IntentCtx } from 'datocms-plugin-sdk';
    connect({
    itemFormSidebars(model: ItemType, ctx: IntentCtx) {
    return [
    {
    id: "sideBySidePreview",
    label: "Side-by-side preview",
    preferredWidth: 900,
    },
    ];
    },
    });

    With the preferredWidth, you can control the ideal width for the sidebar when it opens. Users will then be able to resize it if they want. There is one constraint though: the sidebar width cannot exceed 60% of the screen, taking up too much screen real estate. If the preferredWidth is bigger than this value, it will be capped.

    Rendering the sidebar

    Now, to render the sidebar itself, we can implement the renderItemFormSidebar hook.

    Just like we did with the sidebar panel, we initialize React and render a custom component, passing down as a prop the second ctx argument, which provides a series of information and methods for interacting with the main application:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { connect, RenderItemFormSidebarCtx } from 'datocms-plugin-sdk';
    connect({
    renderItemFormSidebar(
    sidebarId,
    ctx: RenderItemFormSidebarCtx,
    ) {
    ReactDOM.render(
    <React.StrictMode>
    <SideBySidePreviewSidebar ctx={ctx} />
    </React.StrictMode>,
    document.getElementById('root'),
    );
    },
    });

    A plugin might render different sidebars, so we can use the sidebarId argument to know which one we are requested to render, and write a specific React component for each of them.

    In our <SideBySidePreviewSidebar> component, we can simply render an iframe pointing to the webpage, copying most of the logic from our previous sidebar panel:

    import { Canvas } from 'datocms-react-ui';
    type PropTypes = {
    ctx: RenderItemFormSidebarCtx;
    };
    function SideBySidePreviewSidebar({ ctx }: PropTypes) {
    const { permalinksByModel } = ctx.plugin.attributes.parameters;
    const permalinkStructure = permalinksByModel[ctx.itemType.attributes.api_key];
    const url = permalinkStructure.replace(':slug', ctx.formValues.slug);
    return (
    <Canvas ctx={ctx}>
    <iframe src={url} />
    </Canvas>
    );
    }

    itemFormSidebarPanels

    Use this function to declare new sidebar panels to be shown when the user edits records of a particular model.

    Return value

    The function must return an array of objects with the following structure:

    Properties available in context

    The following information and methods are available:

    itemFormSidebars

    Use this function to declare new sidebar to be shown when the user edits records of a particular model.

    Return value

    The function must return an array of objects with the following structure:

    Properties available in context

    The following information and methods are available:

    renderItemFormSidebarPanel

    This function will be called when the plugin needs to render a sidebar panel (see the itemFormSidebarPanels function).

    Properties available in context

    The following information and methods are available:

    Methods available in context

    The following information and methods are available:

    renderItemFormSidebar

    This function will be called when the plugin needs to render a sidebar (see the itemFormSidebars function).

    Properties available in context

    The following information and methods are available:

    Methods available in context

    The following information and methods are available: