Multiple rich text editors on one page: independent fields and form submission

RichTextEditor with its full toolbar

Build a two-editor form with unique IDs, separate HTML values, safe inspection and confirmed per-field reset.

Keep a separate instance for every field

A product form may need a short summary and a longer description. Create one editor per host and retain both references in a map. Load the stylesheet, core and plugins once. Do not reuse a host ID or store every instance in the same variable. Independent instances are separate documents, not a shared-document or multi-root editor.

Run the complete two-editor example

Serve the editor files from your app. The example shows both current HTML values as JSON using textContent, not innerHTML. It does not save or send a request. Restoring only the summary leaves the details untouched; restoring both requires confirmation. The preview clears after changes so it is not mistaken for saved content.

<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<style>body{font:16px/1.6 system-ui;max-width:960px;margin:24px auto;padding:0 16px}section{min-width:0;margin-block:20px}.editor-scroll{max-width:100%;overflow-x:auto}button{min-height:44px;padding:8px 16px;margin:8px 8px 0 0}pre{white-space:pre-wrap;overflow-wrap:anywhere}</style>
<h1>Two editors, two independent fields</h1>
<form id="product-form">
  <section aria-labelledby="summary-heading">
    <h2 id="summary-heading">Summary</h2>
    <div class="editor-scroll"><div id="summary-editor"></div></div>
    <button type="button" id="restore-summary">Restore summary</button>
  </section>
  <section aria-labelledby="details-heading">
    <h2 id="details-heading">Details</h2>
    <div class="editor-scroll"><div id="details-editor"></div></div>
  </section>
  <button type="submit">Inspect both fields</button>
  <button type="reset">Restore both fields</button>
  <p id="status" role="status" aria-live="polite"></p>
  <pre id="payload"></pre>
</form>
<script>
// Load the assets once. Give every host its own unique ID and instance.
const initial = {
  summary: "<p>A short <strong>product summary</strong>.</p>",
  details: "<h2>Details</h2><p>A separate document with its own formatting.</p>"
};
const editors = {
  summary: new RichTextEditor("#summary-editor", { toolbar: "full", height: "240px" }),
  details: new RichTextEditor("#details-editor", { toolbar: "full", height: "320px" })
};
const form = document.getElementById("product-form");
const status = document.getElementById("status");
const output = document.getElementById("payload");
function resetFields() {
  for (const key of Object.keys(editors)) editors[key].setHTMLCode(initial[key]);
}
resetFields();
function invalidatePreview() {
  output.textContent = "";
  status.textContent = "Draft changed. Inspect again to see current values.";
}
for (const instance of Object.values(editors)) instance.attachEvent("change", invalidatePreview);
document.getElementById("restore-summary").addEventListener("click", () => {
  if (!confirm("Restore only the summary?")) return;
  editors.summary.setHTMLCode(initial.summary);
  invalidatePreview();
});
form.addEventListener("reset", event => {
  if (!confirm("Restore both documents?")) { event.preventDefault(); return; }
  resetFields();
  output.textContent = "";
  status.textContent = "Both initial documents restored. Nothing saved.";
});
function collect() {
  return Object.fromEntries(Object.entries(editors).map(([key, instance]) => [key, instance.getHTMLCode()]));
}
form.addEventListener("submit", event => {
  event.preventDefault();
  // Read both live instances now; never use whichever editor last had focus.
  output.textContent = JSON.stringify(collect(), null, 2);
  status.textContent = "Current fields shown below. No request sent.";
});
window.multipleDemo = { editors, initial, collect };
</script>

Store and validate each field explicitly

Use stable field keys such as summary and details in the server contract. Read both instances on submission, even if the user last focused only one. A real endpoint must authorize the record and validate and sanitize each HTML field; a browser preview is not security. If either field is invalid, retain both drafts and show which field needs attention. Decide whether the pair must be saved atomically.

Coordinate autosave and reset

Use a distinct autosave key for each document, or one versioned record for the whole pair. Reusing one local-storage key can overwrite the other draft. Resetting a browser instance does not cancel a server write, delete uploaded files or provide a recovery backup. Apply your existing queue and version policy before enabling autosave here.

Acceptance checklist

Type into each editor and inspect both fields after changing focus. Restore one and confirm that the other HTML is identical. Cancel the full reset and keep both drafts. Test long JSON, emoji, keyboard controls and 320px layouts. Loading many full toolbars costs memory and space: measure your real form before initializing dozens of editors.

Read next