diff --git a/.changeset/thick-years-shop.md b/.changeset/thick-years-shop.md
new file mode 100644
index 0000000000..24cd3e09a4
--- /dev/null
+++ b/.changeset/thick-years-shop.md
@@ -0,0 +1,5 @@
+---
+"@udecode/plate-core": minor
+---
+
+add `normalizeInitialValue` prop to `Plate`. When `true`, it will normalize the initial value passed to the `editor` once it's created. This is useful when adding normalization rules on already existing content. Default is `false`.
diff --git a/docs/docs/guides/Plate.md b/docs/docs/guides/Plate.md
index 1e52a45d54..adccbef38a 100644
--- a/docs/docs/guides/Plate.md
+++ b/docs/docs/guides/Plate.md
@@ -94,6 +94,12 @@ props if `editor` is defined.
- Initial value of the editor.
+### `normalizeInitialValue`
+
+- When `true`, it will normalize the initial value passed to the `editor` once it gets created.
+- This is useful when adding normalization rules on already existing content.
+- Default is `false`.
+
### `options`
- Options stored by plugin key.
diff --git a/packages/core/src/components/Plate.spec.tsx b/packages/core/src/components/Plate.spec.tsx
index 259dda6a19..41739cb984 100644
--- a/packages/core/src/components/Plate.spec.tsx
+++ b/packages/core/src/components/Plate.spec.tsx
@@ -1,5 +1,8 @@
import React from 'react';
import { render } from '@testing-library/react';
+import { isEqual, memoize } from 'lodash';
+import { createEditor, Editor, NodeEntry, Transforms } from 'slate';
+import { PlatePlugin, TEditor } from '../types';
import { Plate } from './Plate';
describe('Plate', () => {
@@ -9,3 +12,88 @@ describe('Plate', () => {
expect(1).toBe(1);
});
});
+
+describe('Plate', () => {
+ it('should trigger normalize if normalizeInitialValue set', () => {
+ const fn = jest.fn((e: TEditor, [node, path]: NodeEntry) => {
+ if (
+ Editor.isBlock(e, node) &&
+ path?.length &&
+ !isEqual((node as any).path, path)
+ ) {
+ Transforms.setNodes(e, { path } as any, { at: path });
+ }
+ });
+
+ const plugins: PlatePlugin[] = memoize((): PlatePlugin[] => [
+ {
+ withOverrides: (e) => {
+ const { normalizeNode } = e;
+ e.normalizeNode = (n: NodeEntry) => {
+ fn(e, n);
+ normalizeNode(n);
+ };
+ return e;
+ },
+ },
+ ])();
+
+ const editor = createEditor();
+
+ render(
+
+ );
+
+ expect(fn).toBeCalled();
+
+ expect(editor.children).toStrictEqual([
+ { children: [{ text: '' }], path: [0] },
+ ]);
+ });
+
+ it('should not trigger normalize if normalizeInitialValue is not set to true', () => {
+ const fn = jest.fn((e: TEditor, [node, path]: NodeEntry) => {
+ if (
+ Editor.isBlock(e, node) &&
+ path?.length &&
+ !isEqual((node as any).path, path)
+ ) {
+ Transforms.setNodes(e, { path } as any, { at: path });
+ }
+ });
+
+ const plugins: PlatePlugin[] = memoize((): PlatePlugin[] => [
+ {
+ withOverrides: (e) => {
+ const { normalizeNode } = e;
+ e.normalizeNode = (n: NodeEntry) => {
+ fn(e, n);
+ normalizeNode(n);
+ };
+ return e;
+ },
+ },
+ ])();
+
+ const editor = createEditor();
+
+ render(
+
+ );
+
+ expect(fn).not.toBeCalled();
+
+ expect(editor.children).not.toStrictEqual([
+ { children: [{ text: '' }], path: [0] },
+ ]);
+ });
+});
diff --git a/packages/core/src/hooks/usePlate/usePlate.ts b/packages/core/src/hooks/usePlate/usePlate.ts
index 684d3e0f85..001c372aa4 100644
--- a/packages/core/src/hooks/usePlate/usePlate.ts
+++ b/packages/core/src/hooks/usePlate/usePlate.ts
@@ -18,6 +18,7 @@ export const usePlate = ({
plugins,
onChange,
editableProps,
+ normalizeInitialValue,
}: UsePlateOptions) => {
usePlateEffects({
id,
@@ -27,6 +28,7 @@ export const usePlate = ({
editor,
value,
options,
+ normalizeInitialValue,
});
return {
diff --git a/packages/core/src/hooks/usePlate/usePlateEffects.ts b/packages/core/src/hooks/usePlate/usePlateEffects.ts
index de71498a14..554d1a86f2 100644
--- a/packages/core/src/hooks/usePlate/usePlateEffects.ts
+++ b/packages/core/src/hooks/usePlate/usePlateEffects.ts
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
-import { createEditor } from 'slate';
+import { createEditor, Editor } from 'slate';
import { createHistoryPlugin } from '../../plugins/createHistoryPlugin';
import { createReactPlugin } from '../../plugins/createReactPlugin';
import { usePlateActions } from '../../stores/plate/plate.actions';
@@ -24,6 +24,7 @@ export const usePlateEffects = ({
components,
options,
initialValue,
+ normalizeInitialValue,
plugins,
}: UsePlateEffectsOptions) => {
const {
@@ -114,4 +115,10 @@ export const usePlateEffects = ({
storePlugins,
setEditor,
]);
+
+ useEffect(() => {
+ if (storeEditor && normalizeInitialValue) {
+ Editor.normalize(storeEditor, { force: true });
+ }
+ }, [storeEditor, normalizeInitialValue]);
};
diff --git a/packages/core/src/types/UsePlateEffectsOptions.ts b/packages/core/src/types/UsePlateEffectsOptions.ts
index bd8fe998f7..7bf299f0a2 100644
--- a/packages/core/src/types/UsePlateEffectsOptions.ts
+++ b/packages/core/src/types/UsePlateEffectsOptions.ts
@@ -28,4 +28,11 @@ export interface UsePlateEffectsOptions
* @see {@link EditorId}
*/
components?: Record;
+
+ /**
+ * When `true`, it will normalize the initial value passed to the `editor` once it gets created.
+ * This is useful when adding normalization rules on already existing content.
+ * @default false
+ */
+ normalizeInitialValue?: boolean;
}