Example policies
Pass any of these policies to ContentProtection.configure({ policy }). The named Nodes, Marks and attributes must exist in your editor's schema.
import type { ContentProtectionPolicy } from '@tiptap-pro/extension-content-protection'Protect headings
const policy = {
version: 1,
rules: [
{
selector: { target: 'node', node: { types: ['heading'] } },
permissions: { edit: false },
},
],
} satisfies ContentProtectionPolicyProtects headings, including their type, text, formatting and attributes. Use target: 'nodeType' instead to keep their structure fixed while allowing text and attribute edits.
Fix heading levels
const policy = {
version: 1,
rules: [
{
selector: {
target: 'attribute',
node: { types: ['heading'] },
names: ['level'],
},
permissions: { edit: false },
},
],
} satisfies ContentProtectionPolicyPrevents changing a heading's level attribute. Its text remains editable, and the heading itself can still be removed.
Keep headings while allowing text edits
const policy = {
version: 1,
rules: [
{
selector: {
target: ['nodeType', 'attribute'],
node: { types: ['heading'] },
names: ['level'],
},
permissions: { edit: false },
},
],
} satisfies ContentProtectionPolicyKeeps headings in place and prevents level changes while allowing their text and formatting to change. Unlike target: 'node', this does not lock the entire heading.
Keep link destinations inside headings
const policy = {
version: 1,
rules: [
{
selector: {
target: ['markType', 'markAttribute'],
mark: {
types: ['link'],
within: { types: ['heading'] },
},
names: ['href'],
},
permissions: { edit: false },
},
],
} satisfies ContentProtectionPolicyPrevents adding or removing links in headings and changing their destinations. Their text and other attributes remain editable. Links outside headings are unaffected. The heading is an ancestor condition; this rule does not protect the heading itself.
See nested ancestor matching for a target array constrained by several ancestors.
Hide blockquotes
const policy = {
version: 1,
rules: [
{
selector: { target: 'node', node: { types: ['blockquote'] } },
permissions: { read: false },
},
],
} satisfies ContentProtectionPolicyReplaces blockquotes with redacted content and prevents editing them. The original content remains in the client-side document.
Allow edits only inside blockquotes
const policy = {
version: 1,
rules: [
{
selector: { target: 'nodeContent', node: { types: ['doc'] } },
permissions: { edit: false },
},
{
priority: 10,
selector: { target: 'nodeContent', node: { types: ['blockquote'] } },
permissions: { edit: true },
},
{
priority: 20,
selector: { target: 'nodeType', node: { types: ['blockquote'] } },
permissions: { edit: false },
},
],
} satisfies ContentProtectionPolicyLocks the document, then allows changes inside existing blockquotes. The final rule protects the blockquotes themselves, including nested ones; their attributes remain protected by the document-wide rule.
Protect nested content
const policy = {
version: 1,
rules: [
{
selector: { target: 'nodeContent', node: { types: ['doc'] } },
permissions: { edit: false },
},
{
priority: 10,
selector: { target: 'nodeContent', node: { types: ['blockquote'] } },
permissions: { edit: true },
},
{
priority: 20,
selector: {
target: 'nodeContent',
node: { types: ['blockquote'], within: { types: ['blockquote'] } },
},
permissions: { edit: false },
},
],
} satisfies ContentProtectionPolicyAllows editing inside outer blockquotes while protecting the contents of nested blockquotes at every depth. The nested deny wins because of its priority, not its selector depth. To protect the nested Nodes' type, existence and attributes too, use target: 'node' for the nested deny.
Protect table columns and headers
const policy = {
version: 1,
rules: [
{
selector: { target: 'children', node: { types: ['tableRow'] } },
permissions: { edit: false },
},
{
selector: { target: 'node', node: { types: ['tableHeader'] } },
permissions: { edit: false },
},
{
selector: {
target: 'attribute',
node: { types: ['tableCell', 'tableHeader'] },
names: ['colspan', 'rowspan'],
},
permissions: { edit: false },
},
],
} satisfies ContentProtectionPolicyProtects the cell sequence in existing rows, header text and header cells. It also prevents changing cell spans. Body-cell content stays editable, and whole body rows can be added or removed.
See the policy language for selector types and rule precedence, or use the Slot filling helper for Slot-only editing.