New in Chrome 124

Here are the highlights in Chrome 124:

Want the full run down? Check out the Chrome 124 Release Notes.

Use declarative shadow DOM in JavaScript

There are two new APIs that allow the declarative shadow DOM to be used from JavaScript.

setHTMLUnsafe() is similar to innerHTML, and lets you set the inner HTML of an element to the string provided. This helps when you have some HTML that includes a declarative shadow DOM, like this.

  <template shadowrootmode="open">
      :host {
        display: block;
        color: yellow;
    Hello, <slot></slot>

If you just use innerHTML, the browser won't parse it properly, and there's no shadow DOM. But with setHTMLUnsafe(), your shadow DOM is created, and the element is parsed as you'd expect.

const section = document.createElement("section");

The other API is parseHTMLUnsafe(), and it works similarly to DOMParser.parseFromString().

Both of these APIs are unsafe, which means they don't do any input sanitization. So you'll want to make sure that whatever you feed them, is safe. In an upcoming release, we expect see a version that does provide sanitization of the input.

Finally, both of these APIs are already supported in the latest version of Firefox and Safari!

WebSocket Stream API

WebSockets are a great way to send data back and forth between the user's browser and the server without having to rely on polling. This is great for things like chat apps, where you want to deal with information as soon as it comes in.

But what if the data arrives faster than you can handle?

That's called back pressure, and can cause some serious headaches for you. Unfortunately, the WebSocket API doesn't have a nice way to deal with back pressure.

The WebSocket Stream API gives you the power of streams, and web sockets, which means back pressure can be applied without any extra cost.

Start by constructing a new WebSocketStream and passing it the URL of the WebSocket server.

const wss = new WebSocketStream(WSS_URL);
const {readable, writable} = await wss.opened;
const reader = readable.getReader();
const writer = writable.getWriter();

while (true) {
  const {value, done} = await;
  if (done) {
  const result = await process(value);
  await writer.write(result);

Next, you wait for the connection to be opened, which results in a ReadableStream and a WritableStream. By calling the ReadableStream.getReader() method, you get a ReadableStreamDefaultReader which you can then read() data from until the stream is done.

To write data, call the WritableStream.getWriter() method, which gives you a WritableStreamDefaultWriter, that you can then write() data to.

View transitions improvements

I'm excited about the View Transitions features, and there are two new features in Chrome 124 designed to make view transitions easier.

The pageswap event is fired on a document's window object when a navigation will replace the document with a new document.

document.addEventListener("pageswap", event => {
  if (!event.viewTransition) {

And document render-blocking which lets you block rendering of a document until the critical content has been parsed, ensuring a consistent first paint across all browsers.

And more!

Of course there's plenty more.

  • disallowReturnToOpener hints to the browser that it shouldn't show a button in the picture-in-picture window that allows the user to go back to the opener tab.

  • Keyboard-focusable scroll containers improves accessibility by making scroll containers focusable using sequential focus navigation.

  • And universal install allows users to install any page, even those not meeting the current PWA criteria.

Further reading

This covers only some key highlights. Check the following links for additional changes in Chrome 124.


To stay up to date, subscribe to the Chrome Developers YouTube channel, and you'll get an email notification whenever we launch a new video.

I'm Pete LePage, and as soon as Chrome 125 is released, we'll be right here to tell you what's new in Chrome!