Content components
Content components are a new-generation of rich-text building blocks that can be used with the Markdoc and MDX fields.
You can define content components by passing a components
object to the Markdoc or MDX field:
// Inside a collection/singleton...
schema: {
// ...
richText: fields.mdx({
label: 'Rich text',
components: {
// Content components here
}
})
}
There are 5 types of content components, listed below.
Refer to the Type signature to learn how to create ContentView
component previews, restrict component usage with forSpecificLocations
, and more.
Wrapper
A wrapper
component has an opening and closing tag, with children
content wrapped inside.
The children
content can be freeform rich text, or a combination of other content components.
Example: Testimonial
import { wrapper } from '@keystatic/core/content-components'
Testimonial: wrapper({
label: 'Testimonial',
schema: {
author: fields.text({ label: 'Author' }),
role: fields.text({ label: 'Role' }),
}
})
The example above will add a 'Testimonial' dropdown to the rich text editor. The output for a Testimonial will look like this (using the MDX field):
<Testimonial author="Jina Dawkins" role="Head of Product Design">
I've been very impressed with the work done by the team in such a short period of time. I'm really proud of everyone's effort and dedication!
</Testimonial>
Example: Multi-variant Container
import { wrapper } from '@keystatic/core/content-components'
Container: wrapper({
label: 'Container',
schema: {
crop: fields.select({
label: 'Crop',
description: 'Max width container and options',
options: [
{label: 'normal', value: 'normal'},
{label: 'narrow', value: 'narrow'},
{label: 'narrower', value: 'narrower'},
{label: 'bleed', value: 'bleed'},
{label: 'boxed', value: 'boxed'},
{label: 'narrow-boxed', value: 'narrow-boxed'},
],
defaultValue: 'normal'
}),
}
})
This Container
component can contain rich text as children
, but also a Testimonial
component or any other existing content component:
<Container crop="narrow">
<Testimonial author="Jina Dawkins" role="Head of Product Design">
I've been very impressed with the work done by the team in such a short period of time. I'm really proud of everyone's effort and dedication!
</Testimonial>
</Container>
Block
A block
component has a self-closing tag, and therefore no children
.
Example
import { block } from '@keystatic/core/content-components'
Playlist: block({
label: 'Playlist',
schema: {
id: fields.text({ label: 'Playlist ID' }),
}
})
The MDX output for an PlayList
will look like this:
<PlayList id="5f8a3b3e3f3e4d001f3e4d00" />
Inline
An inline
component is just like a block
component, but it will sit inline within a paragraph or other text content.
Example
import { inline } from '@keystatic/core/content-components'
StatusBadge: inline({
label: 'StatusBadge',
schema: {
status: fields.select({
label: 'Status',
options: [
{label: 'To do', value: 'todo'},
{label: 'In Progress', value: 'in-progress'},
{label: 'Ready for review', value: 'ready-for-review'},
{label: 'Done', value: 'done'},
],
defaultValue: 'todo'
}),
}
})
The MDX output for a StatusBadge
will look like this:
This task is currently <StatusBadge status="in-progress" /> but has no blocker on the rest of the team.
Mark
The mark
component lets you highlight text
You can select text in the rich text editor and apply a mark
component to it, just like you would apply bold or italic formatting.
Example
import { mark } from '@keystatic/core/content-components'
Highlight: mark({
label: 'Highlight',
schema: {
variant: fields.select({
label: 'Variant',
options: [
{label: 'Fluro', value: 'fluro'},
{label: 'Minimal', value: 'minimal'},
{label: 'Brutalist', value: 'brutalist'},
],
defaultValue: 'fluro'
}),
}
})
The selected text will be wrapped in a Highlight
component:
This is a <Highlight variant="fluro">highlighted</Highlight> word.
Repeating
The repeating
component sets a "0 to many" list of explicitely defined components.
Use this to achieve the pattern of parent/child component composition, where children are responsible for their own data/props:
<Parent>
<Child title="Repeating" data={} />
<Child title="List" data={} />
<Child title="Of" data={} />
<Child title="Things" data={} />
</Parent>
The repeating
components takes a children
array, where you can define which components are allowed to be inserted.
Example
import { repeating } from '@keystatic/core/content-components'
TestimonialGrid: repeating({
label: 'Testimonial Grid',
// Only allow Testimonial components
children: ['Testimonial'],
schema: {
columns: fields.integer({
label: 'Columns',
validation: {
min: 1,
max: 6
}
})
}
}),
Testimonial: wrapper({
label: 'Testimonial',
schema: {
author: fields.text({ label: 'Author' }),
role: fields.text({ label: 'Role' }),
}
})
The MDX output for this TestimonialGrid
will look like this:
<TestimonialGrid columns={2}>
<Testimonial author="Jina Dawkins" role="Head of Product Design">
I've been very impressed with the work done by the team in such a short period of time. I'm really proud of everyone's effort and dedication!
</Testimonial>
<Testimonial author="Leesa Edwards" role="CMO">
The team makes my job easy. I'm just here to amplify the amazing work everyone here is doing!
</Testimonial>
</TestimonialGrid>
Type signature
Find the latest version of the content-components
type signature at: https://docsmill.dev/npm/@keystatic/core@latest#/content-components