Plugin SDK > Sidebars and sidebar panels

Sidebars and sidebar panels

Through plugins it is possible to customize the standard sidebars that DatoCMS offers when editing a record or an asset in the Media Area.

Sidebars vs Sidebar Panels

The SDK offers two ways to customize the sidebar interface. You can either add new collapsible panels to the default sidebar:

Or offer complete alternative sidebars, as in the example below:

Depending on the size of the content you need to display, you can choose one or the other. Or even offer both. 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 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, ItemFormSidebarPanelsCtx } from 'datocms-plugin-sdk';
connect({
itemFormSidebarPanels(model: ItemType, ctx: ItemFormSidebarPanelsCtx) {
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: ItemFormSidebarPanelsCtx) {
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 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, ItemFormSidebarsCtx } from 'datocms-plugin-sdk';
connect({
itemFormSidebars(model: ItemType, ctx: ItemFormSidebarsCtx) {
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>
);
}

Asset Sidebars and Sidebar Panels

In addition to being able to customize the sidebars of a record, it is also possible to do the same in the detail view of an asset in the Media Area:

The implementation is absolutely similar to the one just seen. The only thing that changes is the hooks to be used:

Here's an example:

import {
connect,
UploadSidebarPanelsCtx,
RenderUploadSidebarPanelCtx,
UploadSidebarsCtx,
RenderUploadSidebarCtx
} from 'datocms-plugin-sdk';
connect({
uploadSidebars(ctx: UploadSidebarsCtx) {
return [
{
id: "customSidebar",
label: "My Custom Sidebar",
preferredWidth: 900,
},
];
},
renderUploadSidebar(sidebarId: string, ctx: RenderUploadSidebarCtx) {
render(<CustomSidebar ctx={ctx} />);
},
uploadSidebarPanels(ctx: UploadSidebarPanelsCtx) {
return [
{
id: 'customSidebarPanel',
label: 'Custom Sidebar Panel',
startOpen: true,
},
];
},
renderUploadSidebarPanel(sidebarPanelId: string, ctx: RenderUploadSidebarPanelCtx) {
render(<CustomSidebarPanel ctx={ctx} />);
},
});

Function Reference

uploadSidebars()

Return value

The function must return UploadSidebar[].

Context object

The following properties and methods are available in the ctx argument:

uploadSidebarPanels()

Return value

The function must return UploadSidebarPanel[].

Context object

The following properties and methods are available in the ctx argument:

renderUploadSidebarPanel()

Context object

The following properties and methods are available in the ctx argument:

renderUploadSidebar()

Context object

The following properties and methods are available in the ctx argument:

renderItemFormSidebarPanel()

Context object

The following properties and methods are available in the ctx argument:

renderItemFormSidebar()

Context object

The following properties and methods are available in the ctx argument:

itemFormSidebars()

Return value

The function must return ItemFormSidebar[].

Context object

The following properties and methods are available in the ctx argument:

itemFormSidebarPanels()

Return value

The function must return ItemFormSidebarPanel[].

Context object

The following properties and methods are available in the ctx argument: