Images in strings serialized by serializeHtml are displayed too small
Description
When the string serialized by the serializeHtml() function contains img tags, there will be an inline style width:0 on a parent element of the tag. However, this style will cause the image to be displayed too small.
Steps to Reproduce
// https://github.com/udecode/plate/issues/2804#issuecomment-1931065187
const excludedSelectionPlugin = plugins?.filter(
(plugin) => plugin.key !== "blockSelection"
);
const tmpEditor = createPlateEditor({ plugins: excludedSelectionPlugin });
interface ContentEditorProps {
raw: Value;
onContentUpdate: (raw: Value, html: string) => void;
}
function ContentEditor({ raw, onContentUpdate }: ContentEditorProps) {
const [editorState, setEditorState] = React.useState<Value>([]);
const handleSaveContent = () => {
const html = serializeHtml(tmpEditor, {
nodes: editorState,
dndWrapper: (props) => <DndProvider backend={HTML5Backend} {...props} />,
});
onContentUpdate(editorState, html);
};
return (
<>
<RichTextEditor
onEditorValueChange={setEditorState}
initialValue={raw}
/>
<Button className="my-4" onClick={handleSaveContent}>
save
</Button>
</>
);
}
I use the onContentUpdate function to save data to the database. The raw variable is the original state of the editor, and the html variable is the serialized string of the editor content, which is used to display directly on the page. The data in raw can be used to restore the editor state normally after being read from the API, but the img elements in the serialized html have incorrect inline styles, which causes the images to be displayed too small.
<div class="slate-img"><figure contenteditable="false"><div style="position:relative"><div style="width:0;min-width:92px;max-width:100%;position:relative"><div></div><img src="https://localhost:44392/api/cms-kit/media/9c6dc7ab-07d0-b34d-d549-3a112dffb475" draggable="true" alt="" style="\n"><div></div></div></div></figure></div>
Sandbox
Expected Behavior
The size of images edited in the editor should be the same as the size displayed on the page.
Environment
- slate: "0.102.0"
- slate-react: "0.102.0"
- browser: brave
Bounty
Click here to add a bounty via Algora.
Funding
- You can sponsor this specific effort via a Polar.sh pledge below
- We receive the pledge once the issue is completed & verified
I found a code snippet that I think might be causing the bug. https://github.com/udecode/plate/blob/304fc1ae7b7654008fddf444dfd9a3743cb3b28e/packages/resizable/src/components/Resizable.tsx#L28C1-L67C3
export const useResizableState = ({
align = 'center',
minWidth = 92,
maxWidth = '100%',
}: ResizableOptions = {}) => {
const element = useElement<TResizableElement>();
const editor = useEditorRef();
const nodeWidth = element?.width ?? '100%';
const [width, setWidth] = useResizableStore().use.width();
const setNodeWidth = React.useCallback(
(w: number) => {
const path = findNodePath(editor, element!);
if (!path) return;
if (w === nodeWidth) {
// Focus the node if not resized
select(editor, path);
} else {
setNodes<TResizableElement>(editor, { width: w }, { at: path });
}
},
[editor, element, nodeWidth]
);
React.useEffect(() => {
setWidth(nodeWidth);
}, [nodeWidth, setWidth]);
return {
align,
minWidth,
maxWidth,
setNodeWidth,
setWidth,
width,
};
};
import React from 'react';
import { createAtomStore } from '@udecode/plate-common';
export const { resizableStore, useResizableStore, ResizableProvider } =
createAtomStore(
{
width: 0 as React.CSSProperties['width'],
},
{ name: 'resizable' }
);
When I serialize the editor state to HTML, the value of nodeWidth is the width of the element, or 100% if I have not resize the element, but
useResizableStore always create store with default width value 0, in server rendering, useEffect won't fire. so 0 is the final value get render to static string
To summarize, when I call serializeHtml, and serializeHtml calls renderToStaticMarkup, the rendered image width will always be 0.
I have the same issue. Is there any trick?
I've found a simple workaround that addresses the issue temporarily.
In the image-element.tsx component:
const width = useResizableStore().get.width()
to
const width = props.element.width
// ... rest of the component
<Resizable
align={align}
options={{
align,
readOnly,
}}
**style={{
maxWidth: "100%",
minWidth: "92px",
position: "relative",
width: width + "px",
}}**
>
// ... rest of the component