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 17453_f136c4-72> |
Type 17453_50c816-7f> |
Description 17453_d0c8fa-13> |
---|---|---|
name 17453_cbdc1d-fd> |
string 17453_a4395d-4c> |
The name of the collection (or file for file collections) which this preview component will be used for |
react_component 17453_aa4285-48> | 17453_8cc584-ea> |
A React functional component that renders the collection data. 17453_33c1c0-cc> |
The following parameters will be passed to your react_component
during render:
Param 17453_ada306-bd> |
Type 17453_b522fb-d9> |
Description 17453_cfbf80-08> |
---|---|---|
entry 17453_e8ffac-d3> |
object 17453_eb700d-9c> |
Object with a |
collection 17453_cde485-64> |
object 17453_26a717-d5> |
Collection configuration 17453_8ccdca-4d> |
fields 17453_9c4fca-34> |
object 17453_44c0d1-24> |
The fields for the given collection 17453_68b734-2b> |
document 17453_fde961-7d> |
Document 17453_4dfb52-ec> |
The document object the preview is within. If rendered with a frame, it will be the frame’s document 17453_cbe9ee-86> |
window 17453_d2fe36-02> |
Window 17453_cb9734-e2> |
The window object the preview is within. If rendered with a frame, it will be the frame’s window 17453_754714-4a> |
widgetFor 17453_a872e4-89> |
Function 17453_2a6f60-67> |
Given a field name, returns the rendered preview of that field’s widget and value 17453_c6da85-cd> |
widgetsFor 17453_6bafe7-09> |
Function 17453_335463-23> |
Given a field name, returns the rendered previews of that field’s nested child widgets and values 17453_5bb9b2-cd> |
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);
import CMS, { useMediaAsset } from '@staticcms/core';
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 (
<div>
<h1>{entry.data.title}</h1>
<img src={imageUrl} />
<div className="text">{widgetFor('body')}</div>
</div>
);
};
CMS.registerPreviewTemplate('posts', PostPreview);
import CMS, { useMediaAsset } from '@staticcms/core';
import type { TemplatePreviewProps } from '@staticcms/core';
/**
* The type for 'entry.data'
*/
interface Post {
image: string;
title: string;
body: string;
}
const PostPreview = ({ widgetFor, entry, collection, fields }: TemplatePreviewProps<Post>) => {
const imageField = useMemo(() => fields.find(field => field.name === 'image'), [fields]);
const imageUrl = useMediaAsset(entry.data.image, collection, imageField, entry);
return (
<div>
<h1>{entry.data.title}</h1>
<img src={imageUrl} />
<div className="text">{widgetFor('body')}</div>
</div>
);
};
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);
import CMS from '@staticcms/core';
const AuthorsPreview = ({ widgetsFor }) => {
return (
<div>
{/* This is a static header that would only be rendered once for the entire list */}
<h1>Authors</h1>
{/* Here we provide a simple mapping function that will be applied to each object in the array of authors */}
{widgetsFor('authors').map((author, index) => (
<div key={index}>
<hr />
<strong>{author.data.name}</strong>
{author.widgets.description}
</div>
))}
</div>
);
};
CMS.registerPreviewTemplate('authors', AuthorsPreview);
import CMS from '@staticcms/core';
import type { TemplatePreviewProps } from '@staticcms/core';
interface Author {
name: string;
description: string;
}
/**
* The type for 'entry.data'
*/
interface Authors {
authors: Author[];
}
const AuthorsPreview = ({ widgetsFor }: TemplatePreviewProps<Authors>) => {
return (
<div>
{/* This is a static header that would only be rendered once for the entire list */}
<h1>Authors</h1>
{/* Here we provide a simple mapping function that will be applied to each object in the array of authors */}
{widgetsFor('authors').map((author, index) => (
<div key={index}>
<hr />
<strong>{author.data.name}</strong>
{author.widgets.description}
</div>
))}
</div>
);
};
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);
import CMS from '@staticcms/core';
const GeneralPreview = ({ entry, widgetsFor }) => {
const title = entry.data.site_title;
const posts = entry.data.posts;
return (
<div>
<h1>{title}</h1>
<dl>
<dt>Posts on Frontpage</dt>
<dd>{widgetsFor('posts').widgets.front_limit || 0)}</dd>
<dt>Default Author</dt>
<dd>{widgetsFor('posts').data.author || 'None')}</dd>
</dl>
</div>
);
};
CMS.registerPreviewTemplate('general', GeneralPreview);
import CMS from '@staticcms/core';
import type { TemplatePreviewProps } from '@staticcms/core';
interface Posts {
front_limit?: number;
author?: string;
}
/**
* The type for 'entry.data'
*/
interface GeneralSettings {
site_title: string;
posts: Posts;
}
const GeneralPreview = ({ entry, widgetsFor }: TemplatePreviewProps<GeneralSettings>) => {
const title = entry.data.site_title;
const posts = entry.data.posts;
return (
<div>
<h1>{title}</h1>
<dl>
<dt>Posts on Frontpage</dt>
<dd>{widgetsFor('posts').widgets.front_limit || 0)}</dd>
<dt>Default Author</dt>
<dd>{widgetsFor('posts').data.author || 'None')}</dd>
</dl>
</div>
);
};
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 17453_2aca66-6f> |
Type 17453_511d6c-c0> |
Description 17453_fcd0b7-48> |
---|---|---|
name 17453_c96a61-69> |
string 17453_b8a24d-57> |
The name of the collection (or file for file collections) which this preview component will be used for |
component 17453_1da9d5-20> | 17453_017641-9a> |
A React functional component that renders a preview card for a given entry in your collection 17453_c42ea7-92> |
getHeight 17453_5af44c-68> |
function 17453_31ccb3-7f> |
A function that returns the height for your cards. An object containing the current |
The following parameters will be passed to your react_component
during render:
Param 17453_1cb03d-b7> |
Type 17453_5988d6-c9> |
Description 17453_518073-cc> |
---|---|---|
entry 17453_9d975e-e2> |
object 17453_3eec80-c4> |
Object with a |
widgetFor 17453_01c933-39> |
Function 17453_bee22c-a9> |
Given a field name, returns the rendered preview of that field’s widget and value 17453_14e1d3-d3> |
widgetsFor 17453_053a42-44> |
Function 17453_157dc7-c0> |
Given a field name, returns the rendered previews of that field’s nested child widgets and values 17453_bb8e6c-b5> |
hasLocalBackup 17453_787988-6e> |
boolean 17453_4e61b6-ef> |
Whether the current entry has a local backup 17453_db6ba7-2b> |
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);
import CMS from '@staticcms/core';
const PostPreviewCard = ({ entry, widgetFor }) => {
return (
<div style={{ width: '100%' }}>
{widgetFor('image')}
<div style={{ padding: '16px', width: '100%' }}>
<div
style={{
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'start',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'baseline',
gap: '8px',
}}
>
<strong style={{ fontSize: '24px' }}>{entry.data.title}</strong>
<span style={{ fontSize: '16px' }}>{entry.data.date}</span>
</div>
<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'}
</div>
</div>
</div>
</div>
);
};
CMS.registerPreviewCard('posts', PostPreviewCard, () => 240);
import CMS from '@staticcms/core';
import type { TemplatePreviewCardProps } from '@staticcms/core';
/**
* The type for 'entry.data'
*/
interface Post {
image: string;
title: string;
date: string;
body: string;
draft: boolean;
}
const PostPreviewCard = ({ entry, widgetFor }: TemplatePreviewCardProps<Post>) => {
return (
<div style={{ width: '100%' }}>
{widgetFor('image')}
<div style={{ padding: '16px', width: '100%' }}>
<div
style={{
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'start',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'baseline',
gap: '8px',
}}
>
<strong style={{ fontSize: '24px' }}>{entry.data?.title}</strong>
<span style={{ fontSize: '16px' }}>{entry.data?.date}</span>
</div>
<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'}
</div>
</div>
</div>
</div>
);
};
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 17453_e00e74-5d> |
Type 17453_1fc0ce-1b> |
Description 17453_06931f-41> |
---|---|---|
collectionName 17453_9b6553-87> |
string 17453_b23240-80> |
The name of the collection (or file for file collections) which this preview component will be used for |
fieldName 17453_9a6215-89> |
string 17453_a56163-51> |
The name of the field 17453_9ceefa-c6> |
component 17453_40fc0c-c7> | 17453_b3dc17-51> |
A React functional component that renders a preview of the field for a given entry in your collection 17453_0a97fa-67> |
The following parameters will be passed to your component
during render:
Param 17453_068241-37> |
Type 17453_8c4a41-9e> |
Description 17453_b34c25-c0> |
---|---|---|
collection 17453_34f151-f4> |
object 17453_8ecc8c-31> |
Collection configuration 17453_d24f60-0b> |
field 17453_4c95b2-9d> |
object 17453_64bc79-92> |
Field configuration 17453_7f1c03-c5> |
value 17453_f293b8-4a> |
Function 17453_57ee50-e3> |
The current value of the field for the given entry 17453_af8015-04> |
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);
import CMS from '@staticcms/core';
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);
import CMS from '@staticcms/core';
import type { FieldPreviewProps } from '@staticcms/core';
const CMS = ({ value }: FieldPreviewProps<boolean>) => {
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);
Table View
