React form example
SoEditor 1.3.0
BUILT FOR YOUR CMS
Make room for your content
An article, a product story, or your next announcement. Familiar tools for the work you do every day.
WYSIWYGHTML SourceCMS
Loads on demand · Content stays in this page
Install and run
The /cms components are released in 1.3.0. The existing useSoEditorWorkspace entry remains compatible.
sh
pnpm add @soeditor/editor@1.3.0 @soeditor/react@1.3.0 react react-domSelect “Start editing” to run the published packages. Download complete example sources, extract, run pnpm install and pnpm dev, then open react.html.
Try it
- Edit the article and watch bound HTML update below it.
- Select “Replace HTML” to apply external state, then toggle readonly to stop editing.
- “Submit form” reads canonical textarea HTML. “Reset form” restores the initial value for the current mount.
- “HTML Source” loads Source on demand. “Recreate demo” mounts a new instance with the current value. Closing the demo removes the entire iframe.
Complete example source
ts
import { createElement as h, StrictMode, useRef, useState } from 'react';
import type { FormEvent } from 'react';
import { createRoot } from 'react-dom/client';
import { SoEditor } from '@soeditor/react/cms';
import type { ClassicEditor } from '@soeditor/editor/cms';
import { createEditor, initial, label, options } from './shared.js';
function ArticleForm() {
const [html, setHtml] = useState(initial);
const [readonly, setReadonly] = useState(false);
const [version, setVersion] = useState(0);
const [output, setOutput] = useState('');
const [status, setStatus] = useState(label('正在加载', 'Loading'));
const editor = useRef<ClassicEditor | undefined>(undefined);
const button = (zh: string, en: string, onClick: () => void) =>
h('button', { type: 'button', onClick }, label(zh, en));
return h(
'form',
{
onSubmit: (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setOutput(
String(new FormData(event.currentTarget).get('body')),
);
},
},
h(
'p',
{ className: 'notice' },
label(
'React 组件预览:内容仅保存在本页。',
'React component preview: content stays in this page.',
),
),
h(
'div',
{ className: 'controls' },
button('替换 HTML', 'Replace HTML', () =>
setHtml('<h2>Updated article</h2><p>External value</p>'),
),
button('切换只读', 'Toggle readonly', () => setReadonly(!readonly)),
button('读取 HTML', 'Read HTML', () =>
setOutput(editor.current?.getData() ?? ''),
),
button('HTML Source', 'HTML Source', () => {
void editor.current?.setWorkspaceView('source');
}),
button('重建示例', 'Recreate demo', () => {
editor.current = undefined;
setVersion(version + 1);
}),
h('button', { type: 'submit' }, label('提交表单', 'Submit form')),
h('button', { type: 'reset' }, label('重置表单', 'Reset form')),
),
h('p', { role: 'status' }, status),
h(SoEditor, {
key: version,
name: 'body',
value: html,
onChange: setHtml,
readonly,
options,
createEditor,
onReady: (instance) => {
editor.current = instance;
setStatus(label('就绪', 'Ready'));
document.body.dataset.ready = 'true';
},
onError: (error) => setStatus(String(error)),
}),
h('output', { 'data-bound-value': '' }, html),
h('pre', null, output),
);
}
const host = document.getElementById('app');
if (!host) throw new Error('Missing app host');
const root = createRoot(host);
root.render(h(StrictMode, null, h(ArticleForm)));
window.addEventListener('pagehide', () => root.unmount(), { once: true });ts
import '@soeditor/editor/cms/styles.css';
import '../style.css';
import type { CreateClassicEditorOptions } from '@soeditor/editor/cms';
export const locale =
new URLSearchParams(location.search).get('lang') === 'en' ? 'en' : 'zh-CN';
document.documentElement.lang = locale;
export const label = (zh: string, en: string) => (locale === 'en' ? en : zh);
export const initial = '<h2>CMS article</h2><p>Edit this content.</p>';
export const options: CreateClassicEditorOptions = {
locale,
ariaLabel: label('文章正文', 'Article HTML'),
editingModes: ['wysiwyg', 'source'],
minHeight: 240,
};
export const createEditor = async (
host: HTMLElement,
configuration: CreateClassicEditorOptions,
) => {
const cms = await import('@soeditor/editor/cms/optional');
return cms.createClassicEditor(host, configuration);
};html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex, nofollow" />
<title>SoEditor react demo</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./frameworks/react.ts"></script>
</body>
</html>