Inline editing

Editing text directly on the canvas with v-blokkli-editable, giving a fresh block something to show with editor.mockProps, and restricting where a bundle renders with renderFor.

Three of defineBlokkli's features exist to make the editing experience good rather than merely functional: editing text where it appears, showing something sensible before content exists, and letting one bundle behave differently depending on where it sits.

v-blokkli-editable

v-blokkli-editable:<prop> makes rendered text directly editable on the canvas — the editor types into the page rather than into a sidebar field. The directive argument is the prop name:

Vue
<h2 v-blokkli-editable:headline>{{ props.headline }}</h2>
<p v-blokkli-editable:subline>{{ props.subline }}</p>

On a live page the directive does nothing; the text renders normally. There is no editor code in the live bundle and no wrapper element added to your markup.

Use it for the prose an editor changes most and leave structural props to the sidebar. A headline, a subline, a call-to-action label: inline. A product id, a category slug, a URL: sidebar, where a typo is visible as a field value rather than hidden as body text.

Two practical limits:

  • It edits text, not markup. A prop that holds rich content belongs in a rich-text block, not an inline-editable string.
  • The prop must be rendered as the element's content. Binding it to an attribute — :alt, :title — has nothing on the canvas to click.

editor.mockProps

A freshly dragged block has no content yet, so without help it renders as an empty box. mockProps gives the editor something to show:

TypeScript
defineBlokkli({
  bundle: 'badge',
  editor: {
    mockProps: () => ({ label: 'B2B Supply Solutions' }),
  },
})

This is the first thing to add after bundle. The difference between an editor dragging in a block that says "B2B Supply Solutions" and one that produces a 2-pixel-tall empty element is the difference between a block library that feels finished and one that feels broken.

It's a function, so it can produce something plausible per instance. And it never ships: mockProps is editor-only behaviour, like everything else under editor.

For a block that needs platform data to render at all — a product detail block, say — mock the identifier rather than the data, and let the real composables resolve it:

TypeScript
editor: {
  mockProps: () => ({ productId: '5498' }),
}

The rest of the editor object is in the same spirit: icon for the block picker, editTitle for how the block is labelled in the editor's tree, previewWidth for how wide its preview renders, maxInstances for blocks that should appear once per page, and fieldLayout for how a container's fields are arranged in the editing UI.

renderFor

renderFor restricts where a component is used, so one bundle can render differently depending on its context — its parent block, the field it sits in, or the provider around it.

The case it solves: a product_price widget that needs to look one way inside a compact product card and another way on a full product detail page. Without renderFor you'd need two bundles, which means an editor choosing between "Product Price" and "Product Price (detail)" and sometimes choosing wrong. With it, there's one block in the picker and two components behind it, selected by where the block actually lives.

Reach for it when the content and options are identical and only the presentation differs by context. If the two variants would want different options, they're two bundles.

Keeping the editor honest

A few habits, learned the hard way, that make the difference between a theme editors like and one they work around:

  • Every block gets mockProps. No exceptions. An empty block in the picker reads as a bug.
  • Inline-edit the copy, sidebar the structure. An editor should never have to guess whether to click the page or the panel.
  • Give shells sample state. A cart shell with no cart is unreviewable — see Shells and fields.
  • Write title and description for the editor, not for yourself. They're the block's entire user interface in the picker.
  • Test in the responsive preview, not just the edit canvas. Preview renders under a different provider, which is exactly where container blocks that only handle the edit case fall over.

Next steps

Was this page helpful?