Element: setHTML() method
38 comments
·October 22, 2025_the_inflator
Maybe it is then time for having something that is beyond "use strict" at the beginning auf a JavaScript document as one option to use the statement.
I think a config object in which you define for script options like sanitization and other script configuration might be helpful.
After all, there almost always need to be backward compatibility be ensured, and this might work. I am no spec guy, it is just an idea. React makes use of "use client/server", so this would be more central and explicit.
evilpie
We enabled this by default in Firefox Nightly (only) this week.
spankalee
I'll be very excited to use this in Lit when it hits baseline.
While lit-html templates are already XSS-hardened because template strings aren't forgeable, we do have utilities like `unsafeHTML()` that let you treat untrusted strings as HTML, which are currently... unsafe.
With `Element.setHTML()` we can make a `safeHTML()` directive and let the developer specify sanitizer options too.
StrauXX
Why don't you use DOMPurify right now? It's battle tested and supports configs just like this proposal.
CaptainOfCoit
Really happy to see it, after 25 years (https://www.bugcrowd.com/glossary/cross-site-scripting-xss/) of surviving without it. It always struck me as an obvious missing part of the DOM API, and I still don't know why it took this long time.
But mostly I'm just happy that it's finally here, I do appreciate all the hard work people been doing to get this live.
ibowankenobi
The API design could be better. Document fragments are designed to be reused. It should accept an optional fragment key which accepts a document fragment.If not a fragment, throw, if has children, empty contents first.
spankalee
In what way are document fragments meant to be reused?
They empty their contents into the new parent when they're appended, so they can't be meaningfully appended a second time without rebuilding them.
`<template>` is mean to be reused, since you're meant to clone it in order to use it, and then you can clone it again.
michalpleban
So is this basically a safe version of innerHTML?
Octoth0rpe
Yes, although a slightly more relevant way of putting it would be that it's an inbuilt DOMPurify (dompurify being an npm package commonly used to sanitize html before injecting it).
modinfo
Cursor build a pseudo-sethtml: https://github.com/skorotkiewicz/pseudo-sethtml
redbell
> This feature is not Baseline because it does not work in some of the most widely-used browsers.
This is interesting, but it appears to be in its early days as none of the major browsers seem to support it.. yet.
JadeNB
A sibling comment by evilpie says that it is enabled in Firefox Nightly: https://news.ycombinator.com/item?id=45674985
MarsIronPI
Actually, it exists behind an about:config as far back as 138. So if you enable it, it even works in the current ESR.
draw_down
[dead]
dzogchen
Neat. I think once this is adopted by HTMX (or similar libraries) you don't need to sanitize on the server side anymore?
dylan604
Do you honestly feel that we will ever be in a place for the server to not need to sanitize data from the client? Really? I don't. Any suggestion to me of "not needing to sanitize data from client" will immediately have me thinking the person doing the suggesting is not very good at their job, really new, or trying to scam me.
There's no reason to not sanitize data from the client, yet every reason to sanitize it.
auxiliarymoose
If you sanitize on the server, you are making assumptions about what is safe/unsafe for your clients. It's possible to make these assumptions correctly, but that requires keeping them in sync with all clients which is hard to do correctly.
Something that's sanitized from an HTML standpoint is not necessarily sanitized for native desktop & mobile applications, client UI frameworks, etc. For example, with Cloudflare's CloudBleed security incident, malformed img tags sent by origin servers (which weren't themselves by themselves unsafe in browsers) caused their edge servers to append garbage (including miscellaneous secure data) from heap memory to some requests that got indexed by search engines.
Sanitization is always the sole responsibility of the consumer of the content to make sure it presents any inbound data safely. Sometimes the "consumer" is colocated on the server (e.g. for server rendered HTML + no native/API users) but many times it's not.
strbean
It can be a complicated and error-prone process, mainly in scenarios where you have multiple mediums that require different sanitizers. Obviously you should do it. But in such scenarios, the best practice is to sanitize as close to the place it is used as possible. I've seen terrible codebases where they tried to apply multiple layers of sanitization on user input before storing to the DB, then reverse the unneeded layers before output. Obviously this didn't work.
Point being, if you can move sanitization even closer to where it is used, and that sanitization is actually provided by the standard library of the platform in question, that's a massive win.
immibis
By "sanitise" what's really meant is usually "escape". User typed their display name as <script>. You want the screen to say their display name, which is <script>. Therefore you send <script>. That's not their display name - that's just what you write in HTML to get their display name to appear on the screen. You shouldn't store it in the database in the display_name column.
padjo
Sanitize as close as possible to where it is used is usually best, then you don’t have to keep track of what’s sanitized and what’s not sanitized for very long.
(Especially important if sanitation is not idempotent!)
ishouldbework
> It then removes any HTML entities that aren't allowed by the sanitizer configuration, and further removes any XSS-unsafe elements or attributes — whether or not they are allowed by the sanitizer configuration.
Emphasis mine. I do not understand this design choice. If I explicitly allow `script` tag, why should it be stripped?
If the method was called setXSSSafeSubsetOfHTML sure I guess, but feels weird for setHTML to have impossible-to-override filter.
strbean
This is primarily an ergonomic addition, so it kinda makes sense to me to not make the dangerous footguns more ergonomic in the process. You can still assign `innerHTML` etc. to do the dangerous thing.
meowface
I agree, though I also agree with the parent that the method name is a little bit confusing. "safeSetHTML" or "setUntrustedHTML" or something would be clearer.
strbean
Idk about that, there's a good argument that the most obvious methods should be the safe ones. That's what juniors will probably jump to first. If you need the unsafe ones, you'll probably be able to figure that out and find them quickly.
hsbauauvhabzb
Ideally this should be called dangerouslySetInnerHTML but hindsight blah blah
wewtyflakes
Wouldn't that open the floodgates by allowing code that could itself call `setHTML` again but then further revise the args to escalate its privileges?
jmull
I guess they are going for a safe default... the idea is people who don't carefully read the docs or carefully monitor the provenance of their dynamically generated HTML will probably reach for "setHTML()".
Meanwhile, there's "setHTMLUnsafe()" and, of course, good old .innerHTML.
evilpie
If you want to use an XSS-unsafe Sanitizer you have to use setHTMLUnsafe.
null
AlienRobot
Great functionality, terrible name.
varun_ch
I sometimes wonder whether what the DOM APIs could look like in a hypothetical world where we could start over with everything.
As someone who has dealt with more than my fair share of content injection vulnerabilities over the years this is great to see at last. It’s kinda crazy that this only coming now while other, more cumbersome solutions like CSP have been around for years.