Locked areas
Lock a block's slot so authors keep its structure, and choose what they may still change.
A block can lock one of its slots. A locked slot holds the blocks it starts with, the
ones its defaultBlock creates when the block is placed, so a designed section keeps
its shape however an author edits the page. The locks use WordPress's templateLock
names and meanings, so authors who know one know the other.
The four locks
import { defineBlock } from "@nextlyhq/plugin-sdk/blocks";
export const hero = defineBlock({
name: "acme/hero",
version: 1,
description: "A headline and a call to action, in that order.",
example: { props: {} },
slots: {
content: {
defaultBlock: [{ type: "acme/headline" }, { type: "acme/button" }],
lock: "all",
},
},
render: ({ renderSlot }) => renderSlot("content"),
});lock | Blocks can be added or removed | Blocks can be moved | What can change |
|---|---|---|---|
"insert" | no | yes | content and design |
"all" | no | no | content and design |
"contentOnly" | no | no | content only |
false | yes | yes | anything (the slot is unlocked) |
A slot that declares no lock takes the lock of the slot its block sits in, so a locked
section stays locked all the way down. false stops that, except beneath
"contentOnly", which nothing inside it can loosen.
Declare what is content
Inside a "contentOnly" slot, only the props a block declares as content can change:
its text, its media, its links. Mark them with role: "content", as a WordPress block
attribute is marked.
import { defineBlock } from "@nextlyhq/plugin-sdk/blocks";
export const headline = defineBlock({
name: "acme/headline",
version: 1,
description: "A heading.",
example: { props: { text: "Title", align: "left" } },
props: {
text: { type: "text", role: "content" },
align: { type: "select" },
},
defaultProps: { text: "Title", align: "left" },
render: ({ props }) => props.text,
});The core blocks already declare theirs: a heading's text and link, an image's media, alt text and caption, a button's label and link, and so on.
Everything else stays as the block started: its other props, its styles, classes, its
root element's id and attributes, visibility, custom CSS and its own lock. So does whether
it renders: a block marked as failing its migration, or at a version ahead of its
definition's, is drawn as a placeholder, and neither can change there. A block that
declares no content props has nothing an author can change inside a "contentOnly" slot.
Its layer name stays editable, since the page never draws it.
Who is held to a lock
Every save, whoever sends it: the editor, the API, an import or an agent. A save that breaks a lock is refused with an error naming the slot and the rule, for example:
The "content" slot of a "acme/hero" is locked "all", so it holds "acme/headline", "acme/button", in that order. It holds "acme/headline".
A lock is measured against the blocks the slot starts with, so changing a locked slot's
defaultBlock changes what every stored page must hold. Bump the block's version and
migrate the pages already stored.
Changing a block's defaultProps doesn't lock existing pages out. Inside a
"contentOnly" slot, a save may keep any value the page already stores in the same
place, or put it back as the block now starts. Only a third value is refused. A page
saved for the first time, a single, and a localized blocks field are measured against
the starting blocks alone.
A page placing a component keeps what the component itself holds.
Components and locks
A lock inside a component holds on every page that places it. A placement cannot use an exposed field or an exposed slot to change what the lock holds, and such a page is refused, naming the placement.
A component cannot add an exposed field that drives something a "contentOnly" lock
holds, since no placement could change it. Expose a content field instead, remove the
exposure, or loosen the lock on that slot. An exposure a component already stores is
kept, and reported in the server log, so an unrelated edit still saves.
Registration refuses a lock value other than the four, and a role other than
"content", naming the block. nextly generate refuses them too, so a block manifest
is never written for blocks the app would refuse at boot.