---
title: "Example policies"
description: "Simple and combined policies for headings, attributes, hidden content, templates and tables."
canonical_url: "https://tiptap.dev/docs/composable-docs/content-protection/guides/example-policies"
---

# Example policies

Simple and combined policies for headings, attributes, hidden content, templates and tables.

Pass any of these policies to `ContentProtection.configure({ policy })`. The named Nodes, Marks and attributes must exist in your editor's schema.

```ts
import type { ContentProtectionPolicy } from '@tiptap-pro/extension-content-protection'
```

## Protect headings

```ts
const policy = {
  version: 1,
  rules: [
    {
      selector: { target: 'node', node: { types: ['heading'] } },
      permissions: { edit: false },
    },
  ],
} satisfies ContentProtectionPolicy
```

Protects 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

```ts
const policy = {
  version: 1,
  rules: [
    {
      selector: {
        target: 'attribute',
        node: { types: ['heading'] },
        names: ['level'],
      },
      permissions: { edit: false },
    },
  ],
} satisfies ContentProtectionPolicy
```

Prevents changing a heading's `level` attribute. Its text remains editable, and the heading itself can still be removed.

## Keep headings while allowing text edits

```ts
const policy = {
  version: 1,
  rules: [
    {
      selector: {
        target: ['nodeType', 'attribute'],
        node: { types: ['heading'] },
        names: ['level'],
      },
      permissions: { edit: false },
    },
  ],
} satisfies ContentProtectionPolicy
```

Keeps 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

```ts
const policy = {
  version: 1,
  rules: [
    {
      selector: {
        target: ['markType', 'markAttribute'],
        mark: {
          types: ['link'],
          within: { types: ['heading'] },
        },
        names: ['href'],
      },
      permissions: { edit: false },
    },
  ],
} satisfies ContentProtectionPolicy
```

Prevents 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](https://tiptap.dev/docs/composable-docs/content-protection/api-reference/policy.md#nested-ancestor-matching) for a target array constrained by several ancestors.

## Hide blockquotes

```ts
const policy = {
  version: 1,
  rules: [
    {
      selector: { target: 'node', node: { types: ['blockquote'] } },
      permissions: { read: false },
    },
  ],
} satisfies ContentProtectionPolicy
```

Replaces blockquotes with [redacted content](https://tiptap.dev/docs/composable-docs/content-protection/api-reference/rendering.md) and prevents editing them. The original content remains in the client-side document.

## Allow edits only inside blockquotes

```ts
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 ContentProtectionPolicy
```

Locks 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

```ts
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 ContentProtectionPolicy
```

Allows 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

```ts
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 ContentProtectionPolicy
```

Protects 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](https://tiptap.dev/docs/composable-docs/content-protection/api-reference/policy.md) for selector types and rule precedence, or use the [Slot filling helper](https://tiptap.dev/docs/composable-docs/slots/api-reference/utilities.md#createslotfillingpolicy) for Slot-only editing.
