Creating Custom Previews
Static CMS exposes a window.CMS
global object that you can use to register custom previews for an entire collection (or file within a file collection) via registerPreviewTemplate
(editor view) and registerPreviewCard
/ registerFieldPreview
(collection view).
React Components Inline
registerPreviewTemplate
, registerPreviewCard
and registerFieldPreview
require you to provide a React component. If you have a build process in place for your project, it is possible to integrate with this build process.
However, although possible, it may be cumbersome or even impractical to add a React build phase. For this reason, Static CMS exposes some constructs globally to allow you to create components inline: h
(alias for React.createElement) as well some basic hooks (useState
, useMemo
, useEffect
, useCallback
).
Editor Preview
registerPreviewTemplate
allows you to create a template that overrides the entire editor preview for a given collection.
Params
Param | Type | Description |
---|---|---|
name | string | The name of the collection (or file for file collections) which this preview component will be used for
|
react_component | React Function Component | A React functional component that renders the collection data. |
The following parameters will be passed to your react_component
during render:
Param | Type | Description |
---|---|---|
entry | object | Object with a data field that contains the current value of all widgets in the editor |
collection | object | Collection configuration |
fields | object | The fields for the given collection |
document | Document | The document object the preview is within. If rendered with a frame, it will be the frame's document |
window | Window | The window object the preview is within. If rendered with a frame, it will be the frame's window |
widgetFor | Function | Given a field name, returns the rendered preview of that field's widget and value |
widgetsFor | Function | Given a field name, returns the rendered previews of that field's nested child widgets and values |
Example
const PostPreview = ({ widgetFor, entry, collection, fields }) => {
const imageField = useMemo(() => fields.find(field => field.name === 'image'), [fields]);
const imageUrl = useMediaAsset(entry.data.image, collection, imageField, entry);
return h(
'div',
{},
h('h1', {}, entry.data.title),
h('img', { src: imageUrl }),
h('div', { className: 'text' }, widgetFor('body')),
);
};
CMS.registerPreviewTemplate('posts', PostPreview);
Lists and Objects
The API for accessing the individual fields of list- and object-type entries is similar to the API for accessing fields in standard entries, but there are a few key differences. Access to these nested fields is facilitated through the widgetsFor
function, which is passed to the preview template component during render.
List Template Example
For list fields, the widgetFor function returns an array of objects that you can map over in your template. If your field is a list of authors containing two entries, with fields name
and description
, the return value of widgetsFor
would look like this:
[{
data: { title: 'Mathias', description: 'Co-Founder'},
widgets: { title: <WidgetComponent>, description: <WidgetComponent>}
},
{
data: { title: 'Chris', description: 'Co-Founder'},
widgets: { title: <WidgetComponent>, description: <WidgetComponent>}
}]
const AuthorsPreview = ({ widgetsFor }) => {
return h(
'div',
{},
// This is a static header that would only be rendered once for the entire list
h('h1', {}, 'Authors'),
// Here we provide a simple mapping function that will be applied to each
// object in the array of authors
widgetsFor('authors').map(function (author, index) {
return h(
'div',
{ key: index },
h('hr', {}),
h('strong', {}, author.data.name),
author.widgets.description,
);
}),
);
};
CMS.registerPreviewTemplate('authors', AuthorsPreview);
Object Example
Object fields are simpler than lists - instead of widgetsFor
returning an array of objects, it returns a single object. Accessing the shape of that object is the same as the shape of objects returned for list fields:
{
data: { front_limit: 0, author: 'Chris' },
widgets: { front_limit: <WidgetComponent>, author: <WidgetComponent>}
}
const GeneralPreview = ({ entry, widgetsFor }) => {
const title = entry.data.site_title;
const posts = entry.data.posts;
return h(
'div',
{},
h('h1', {}, title),
h(
'dl',
{},
h('dt', {}, 'Posts on Frontpage'),
h('dd', {}, widgetsFor('posts').widgets.front_limit || 0),
h('dt', {}, 'Default Author'),
h('dd', {}, widgetsFor('posts').data.author || 'None'),
),
);
};
CMS.registerPreviewTemplate('general', GeneralPreview);
Editor Preview Styles
Register a custom stylesheet to use on the preview pane.
CMS.registerPreviewStyle(url);
Raw Styles
If you want to provide a raw CSS string instead of a url, you can pass { raw: true }
as the second parameter.
CMS.registerPreviewStyle('.main { color: blue; border: 1px solid gree; }', { raw: true });
Collection Card Preview
registerPreviewCard
allows you to create a card template that overrides the cards displayed in the collection view.
Params
Param | Type | Description |
---|---|---|
name | string | The name of the collection (or file for file collections) which this preview component will be used for
|
component | React Function Component | A React functional component that renders a preview card for a given entry in your collection |
getHeight | function | A function that returns the height for your cards. An object containing the current collection and entry are passed into the function at render. If no getHeight function is provided, the height will be 204 if the collection has an image field or 64 if the collection does not have an image field |
The following parameters will be passed to your react_component
during render:
Param | Type | Description |
---|---|---|
entry | object | Object with a data field that contains the current value of all widgets in the editor |
widgetFor | Function | Given a field name, returns the rendered preview of that field's widget and value |
widgetsFor | Function | Given a field name, returns the rendered previews of that field's nested child widgets and values |
hasLocalBackup | boolean | Whether the current entry has a local backup |
Example
const PostPreviewCard = ({ entry, widgetFor }) => {
return h(
'div',
{ style: { width: '100%' } },
widgetFor('image'),
h(
'div',
{ style: { padding: '16px', width: '100%' } },
h(
'div',
{
style: {
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'start',
},
},
h(
'div',
{
style: {
display: 'flex',
flexDirection: 'column',
alignItems: 'baseline',
gap: '8px',
},
},
h('strong', { style: { fontSize: '24px' } }, entry.data.title),
h('span', { style: { fontSize: '16px' } }, entry.data.date),
),
h(
'div',
{
style: {
backgroundColor: entry.data.draft === true ? 'blue' : 'green',
color: 'white',
border: 'none',
padding: '4px 8px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
},
},
entry.data.draft === true ? 'Draft' : 'Published',
),
),
),
);
};
CMS.registerPreviewCard('posts', PostPreviewCard, () => 240);
Grid View
Collection Field Preview
registerFieldPreview
allows you to create a custom preview for a specific field in the table view for collections.
Params
Param | Type | Description |
---|---|---|
collectionName | string | The name of the collection (or file for file collections) which this preview component will be used for
|
fieldName | string | The name of the field |
component | React Function Component | A React functional component that renders a preview of the field for a given entry in your collection |
The following parameters will be passed to your component
during render:
Param | Type | Description |
---|---|---|
collection | object | Collection configuration |
field | object | Field configuration |
value | Function | The current value of the field for the given entry |
Example
const PostDraftFieldPreview = ({ value }) => {
return h(
'div',
{
style: {
backgroundColor: value === true ? 'rgb(37 99 235)' : 'rgb(22 163 74)',
color: 'white',
border: 'none',
padding: '2px 6px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
fontSize: '14px',
},
},
value === true ? 'Draft' : 'Published',
);
};
CMS.registerFieldPreview('posts', 'draft', PostDraftFieldPreview);