Online HTML Editor
Write or paste HTML, see it rendered, and take it back tidied and sanitised against the allow-list WordPress itself uses.
Enable JavaScript to clean your own HTML; the result below is the example.
Nothing is uploaded. The parsing, the cleaning and the preview all happen in this tab.
Dropped 1 <script>. Unwrapped <x-widget>, keeping the text inside. Removed 1 inline event handler. Removed 1 link or source with a scheme this does not allow.
A pasted card
Some bold text, a refused link and a real one.
- One
- Two
whitespace here is content
<div class="card">
<h2 style="color:#1E56EC">A pasted card</h2>
<p>
Some <b>bold</b> text, a <a>refused link</a> and a <a href="/tools/" target="_blank" rel="noopener">real one</a>.
</p>
<ul>
<li>One</li>
<li>Two</li>
</ul>
Words inside an element nothing renders.
<pre> whitespace here
is content </pre>
</div>
Write HTML on the left, watch it render on the right, take it back indented and
cleaned. The cleaning is the useful part: pasted HTML arrives with inline event
handlers, an editor’s leftover attributes, a <script> nobody wanted and a
<div> wrapped around nothing.
The allow-list here is close to the one WordPress applies with wp_kses_post, so
what comes out is roughly what would survive being saved into a post anyway. The
difference is that you see what was removed.
How to use
- Type or paste. The preview and the output keep up.
- Leave Sanitise on. It holds the output to the allow-list and reports what it took out.
- Turn Indent off when the HTML is going into a template and you want one line.
- Copy the result.
The preview is always sanitised, whatever the checkbox says. It is rendered in
this page, so a <script> in it would be a script in this page.
Example
This goes in:
<div class="card" onclick="track()">
<h2 STYLE="color:#1E56EC">A pasted card</h2>
<p>Some <B>bold</B> text and a <a href="javascript:alert(1)">refused link</a>.</p>
<ul><li>One<li>Two
</ul>
<script>track();</script>
<x-widget>Words inside an element nothing renders.</x-widget>
</div>
and this comes out:
<div class="card">
<h2 style="color:#1E56EC">A pasted card</h2>
<p>Some <b>bold</b> text and a <a>refused link</a>.</p>
<ul>
<li>One</li>
<li>Two</li>
</ul>
Words inside an element nothing renders.
</div>
with the report:
Dropped 1 <script>. Unwrapped <x-widget>, keeping the text inside.
Removed 1 inline event handler. Removed 1 link or source with a scheme this does not allow.
Four different treatments in one paste, and each one is a decision:
<script>goes with its contents. Keeping the contents would put its source code in the page as text.<x-widget>is unwrapped, not dropped. It is not dangerous, it is just not something a browser renders, and the words inside it are the content.onclickgoes;classstays. Thestylestays too, because it only names a colour.- The
javascript:href goes and the link stays, so you can see where it was.
The unclosed <li> elements are closed and the tag names are lower-cased, because
the output is written from a parsed tree rather than patched as text.
Pitfalls
Sanitising off does not turn off the preview’s sanitising. The output panel
will give you your <script> back; the preview never renders it. That is not a
limitation, it is the only safe way to render a stranger’s HTML in a page.
Whitespace is sometimes content. Inside <pre> and <textarea> it is left
exactly as written. Between inline elements a line break is a word gap, so
<b>a</b> <i>b</i> is never broken across lines. Everything else is free to be
indented.
An <iframe> is dropped, including a video embed. The allow-list does not
carry it, because an iframe is a page inside your page. If you need one, paste the
output into a context that allows it, or use the WordPress embed block.
A style attribute is kept if it is only styling. One that names
expression() or a javascript: URL is dropped whole rather than patched, since
a repaired declaration says nothing useful.
This is not a WYSIWYG editor. There are no toolbars: you write the HTML. For writing prose, the markdown editor on this site is the better tool, and it produces HTML too.
Compatibility
The parser handles what pasted markup actually contains: unclosed <li> and
<p>, a closing tag nobody opened, uppercase tag names, and a < inside a
<script> that is not a tag. It is not a conformant HTML5 parser and does not
try to be.
The allow-list covers the elements and attributes wp_kses_post allows, plus
aria-* and data-*, minus the ones that only make sense in a form. That is a
deliberate subset: it is what content in a WordPress post can contain.
The output is HTML5 with void elements self-closed (<br />), which is valid in
both HTML and XHTML. Attribute values are always quoted, including empty ones.
Rendering happens in the browser. The page also renders the example on the server with the same rules, so it reads correctly without JavaScript.
Frequently asked questions
Is this the same as running wp_kses_post?
Why was my <div> kept but my <section> unwrapped?
<font>,
<center> and <marquee> are the usual ones.Can I keep the indentation I already have?
Does it fix broken HTML?
</div>
was meant to go: it closes it at the end of its parent.