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.);
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!}
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>);}
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 persistedreturn <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>);}
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.
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>);}
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:
For sidebars: uploadSidebars
and renderUploadSidebar
For sidebar panels: uploadSidebarPanels
and renderUploadSidebarPanel
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} />);},});
uploadSidebars()
The function must return UploadSidebar[]
.
The following properties and methods are available in the ctx
argument:
uploadSidebarPanels()
The function must return UploadSidebarPanel[]
.
The following properties and methods are available in the ctx
argument:
renderUploadSidebarPanel()
The following properties and methods are available in the ctx
argument:
renderUploadSidebar()
The following properties and methods are available in the ctx
argument:
renderItemFormSidebarPanel()
The following properties and methods are available in the ctx
argument:
renderItemFormSidebar()
The following properties and methods are available in the ctx
argument: