Broken shadow DOM createTweet rendering in Chrome



We embed tweet links inline on our service: Since the switch to shadow DOM, embedded tweets are rendering really tall with lots of whitespace in Chrome on OS X. I had to split it into 4 separate screenshots to fully show it.

Anything I can do to debug? I’ve looked in the Chrome inspector but not sure what should be there.


Forgot to add, we’re creating these embeds with the widgets API:

twttr.widgets.createTweet(tweet_id, wrapperEl, function (widget) {
    // etc


Is the page using CSP? If so, what is it disallowing?

And this is very hacky, but one way to force embedded Tweets to render in srcless iframes is to do the following:

delete Element.prototype.createShadowRoot;

I’m assuming you don’t have any other code running on your page that uses shadow DOM so it could be ok temporarily. In the long run, it’d be best to identify the root cause and fix that (whether it’s originating from widgets.js or from something on your page).


Not using CSP, nope. I’ll try disabling shadow DOM in production for now. An option for this would be good too.

If you want to test this yourself, create a free account on, join an empty channel on freenode and send a tweet link, or PM yourself.

Options we’re using are:

    linkColor: '',
    theme: '' | 'dark',
    dnt: true,
    conversation: 'all',
    cards: 'all',
    related = 'irccloud'

Let me know if you need me to try anything else.


Looks like it’s back to using iframes without that hack now. Is that a temporary rollback or just progressive rollout flip flopping?


+1, tweets were broken for Sway this morning and we also see that it is working again. Is there a way to preview the new Shadow DOM implementation before the wide roll-out?


I did a temporary rollback for a different issue. I’ll be redeploying this afternoon and that deploy will reenable shadow dom usage. Before doing so though, I’ll spend some time debugging both of the issues reported in this thread.

@AdamTReineke can you provide me a link to a page on Sway that contains embedded tweets? I can use that as a testing page to figure out what’s going on for your case.


One of our engineers managed to save a copy of the widgets.js file with Shadow DOM enabled, so I’ll use that to test for now. Looks like one thing we’re doing wrong is adding the widgets.js file in the head, I’ll move that to the body. We’ll also use the hack you listed above to mask shadow DOM support. I expect that change will be deployed in the next couple hours.

Sample Sway with tweets:

The frame we load tweets in: (The three parameters in the hash are tweet ID, width, and height.)


This isn’t necessarily wrong – we should support this and that’s the reason I rolled back the deploy. However, it is better performance-wise to not block the page on widgets.js so moving it to the body or using the async tag is something you should continue forward with.


We are also seeing an issue with createShadowRoot, using the same method to embed the tweet. Error is “Cannot read property createShadowRoot of null”. This started occurring yesterday.

This is currently fixed since the rollback, we plan to implement the hack above to force the iFrame.

Could this be due to the note on MDN that support for multiple shadow roots has been deprecated since Chrome 45? (We had several tweet widgets on the page)


This is a bug on our side of things that I’m currently patching. You shouldn’t see this error in the future.

We were using document.body.createShadowRoot to check for shadow DOM compatibility, but if you load widgets.js synchronously in the HEAD then document.body will be null. I’m currently changing how we detect shadow DOM support so that it will not lead to an error in this scenario.

For those of you who ran into the Cannot read property createShadowRoot of null issue, I would recommend throwing an async attribute on the widgets.js script tag or moving the script tag to the bottom of the BODY. This should lead to your page rendering a tad bit quicker.


@JakeHarding - Here’s the update to our TwitterFrame.html that we’re beginning to deploy. I’m using your hack above to hide Shadow DOM support since some of our JS still throws on it (I’ll remove the hack this week) and I’ve updated how we include widgets.js to use the window.twttr = (function(d, s, id) { ... }(document, "script", "twitter-wjs"); method from the docs.

Thanks for the updates here.


I’d definitely remove the hack ASAP since it modifies a host property.

FWIW I was playing around with the pages you shared and they should work just fine with shadow DOM once the next deploy goes out.

When the next deploy goes out (in an hour or two), you’ll be about to disable shadow DOM usage by way of the twitter:widgets:csp metatag. In order to load the CSS for embedded tweets when rendered using shadow DOM, we rely on inline styles. Occasionally pages disable the usage of inline styles through their CSP configuration. We attempt to detect this on our own, but we use the twitter:widgets:csp metatag as a way for the developer to directly communicate that some restrictions are being applied through CSP.

Taking advantage of the twitter:widgets:csp metatag is a much better option then modifying Element.prototype.createShadowRoot (modifying host objects is rarely a good idea). However, this should only be used as a stopgap if you’re experiencing issues with shadow DOM. Ideally you should allow widgets.js to determine how embedded tweets should be rendered otherwise you run the risk of missing out on future performance optimizations.

Add target="_blank" to links in embedded tweets

The layout seems to be fixed for us using shadow DOM now. Thanks!


Things still broke for me for some reason.
Wrote a little hack to fix this for now.


@sankalp_sans You should not need that hack. The real reason is that floats are not being cleared. I’m not entirely certain why it works with iframes and only breaks with Shadow DOM. This css on the parent div for widgets seems enough:

/* remove hacks in your gist */
.embeddedContent {
  clear: both;

Hope that helps your case.


This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.