Author components for the editor
The visual editor lets editors add blocks to a page. A block is a component. This guide shows how to make your own component a block. It works for docs pages and for other pages of a Nuxt Content site.
@plutocms/docs 0.16 or newer.Where the file goes
Put the component in components/content/. This is the folder where Nuxt Content looks for the components that Markdown uses.
The folder can be in your app or in a layer. The editor reads the folder of the app and the folder of every layer. If two files have the same name, the app wins.
The editor skips two kinds of file. It skips Prose* files, because they replace the Markdown elements. It also skips .client and .server variants.
Add the macro
A component shows in the palette only when it calls definePlutoBlock. A component without the call still works on a page. It is just not in the palette.
The macro is an auto-import. Call it in <script setup>. You do not need an import line.
This is PlutoChip, from this site:
<script setup lang="ts">
definePlutoBlock<{ icon?: string, center?: boolean }>({
label: 'Chip',
icon: 'i-lucide-tag',
group: 'landing',
description: 'A short label above a section title.',
props: {
center: { label: 'Center the label' }
},
slots: { default: { label: 'Text', content: 'inline' } },
insert: '::pluto-chip{icon="i-lucide-heart"}\nA short label\n::'
})
withDefaults(defineProps<{
/** The icon before the text, such as `i-lucide-heart`. */
icon?: string
/** Put the label in the middle of the line. */
center?: boolean
}>(), {
icon: undefined,
center: false
})
</script>
All keys are optional. definePlutoBlock({}) is valid. The props and the slots still come from the component.
| Key | What it does |
|---|---|
label | The name in the palette. Default: the component name in words. |
description | The short text under the name in the palette. |
icon | An Iconify name, such as i-lucide-tag. |
group | The palette group: text, callouts, layout, code, landing, or site. |
props | Changes to single props. See below. |
slots | The slots, with a label, a content kind, and accepts. |
insert | The Markdown that the palette adds. |
preview | false shows a card in the preview, not the component. |
hidden | true keeps the block out of the palette. |
The generic <{ icon?: string, center?: boolean }> is optional. With it, TypeScript checks that each key of props is a prop of the component.
Props become fields
The editor reads the types of defineProps. Each prop with a known type gets a field in the Settings section.
| Type | Field |
|---|---|
string | One line of text. |
string named icon, or ending in Icon | An icon field with a preview. |
string named to or href | A link field. |
number | A number field. |
boolean | A switch. |
A union of string literals, such as 'slow' | 'fast' | A list of choices, in the order of the type. |
| A union of theme color names | A list of the theme colors. |
| An object, an array, or a function | No field. |
A prop with no field is not in the panel. If a page sets it, the prop shows in the Advanced section.
To choose the field yourself, set type in the entry of the prop in props. The types are string, text (a box with more lines), boolean, number, select (add options), icon, color, and link.
To move a prop to Advanced, set advanced: true in its entry in props.
Help text from JSDoc
Write a JSDoc comment above a prop. The editor shows the text as help under the field.
defineProps<{
/** How many layers to show. */
limit?: number
}>()
Keep the text short. Use plain words.
Defaults
A simple default shows in the field. A number shows as the placeholder. A list of choices shows the value in the Default item. A switch shows its default state.
withDefaults(defineProps<{
/** How many layers to show. */
limit?: number
}>(), { limit: 6 })
The default never goes into the Markdown. A page keeps its text until an editor changes the field.
A default must be a plain value, such as 6, 'slow', or false.
Slots
The editor reads the slots of the template. Each slot gets a field in the Content section.
A slot that has the mdc-unwrap attribute gets a one-line field. The attribute removes the paragraph around the text. Use it for a title or a label.
<slot mdc-unwrap="p" />
A slot without mdc-unwrap gets a text box, or a list of blocks, based on the content of the page.
To set the field yourself, use slots in the macro. Use content: 'blocks' for a list of blocks. Add accepts to limit the components that can go in the list.
slots: {
links: { label: 'Buttons', content: 'blocks', accepts: ['u-button'] }
}
PlutoHero on this site uses this for its buttons.
Change a block in app.config.ts
The macro is the main place for the settings of a block. The site can still override it. Put an entry in plutoBlocks in app.config.ts. It wins over the macro. Set a prop to false to remove its field.
export default defineAppConfig({
plutoBlocks: {
'pluto-chip': {
label: 'Tag',
props: { center: false }
}
}
})
The entry changes only the keys that it names. The other keys keep the value from the component. An entry can also add a component that does not call the macro.
The order, from lowest to highest, is:
- The preset of the layer.
- The component: its types and its macro.
plutoBlocksinapp.config.ts.
Labels in more than one language
A label or a description can be plain text or an i18n key. If the key exists in your locale files, the editor shows the translation. If not, it shows the text.
The cache
The build reads the macro in each file. It keeps the result in .nuxt/cache/pluto-blocks.
In dev, a change to a component file updates the palette. A change to a file that the component imports does not. This is true for a shared type file. After such a change, delete the .nuxt/cache/pluto-blocks folder and restart the dev server.
Limits
- The argument of
definePlutoBlockmust be an object literal. It can use constants that you import from a.tsfile. It cannot use a variable from<script setup>. - If the build cannot read the call, it logs one warning with the file name. The component is not in the palette. The rest of the build goes on.
- To turn off the discovery, set
plutoDocs.blocks.discovertofalse.