Sometimes you just want to add a Tweet to a post. Should be simple, right? With .mdx you have some different choices that are worth considering.
Date:
Words: 2070
Time: 8 min read
2024-06-16 Update: OK, just use react-tweet. Somehow it gets around the twitter API debacle and does everything my custom component did including supporting light and dark modes. Just use it!
2023-06-01 Update: "X" has cut off free api access. You have to pay $100/mo for API access now so I no longer endorse this approach.
So, you are using Next.js with .mdx to render static pages and you want to embed a Tweet? What if you simply paste in Twitter's "embed code" - all done? You can also use the Tweet component from mdx-embed, which is a React component that you can use in your .mdx files. Or, you can roll you own for an even better solution.
Let's explore the options.
1
Simply paste the twitter embed code
If you simply paste the twitter embed code into your .mdx file, you will get a Tweet. It may not look great, and it will throw errors. Let's fix that.
Since .mdx is a combination of Markdown and JSX we have to follow JSX standards for embedded HTML. So the "class" attribute needs to be changed to "className". Then "charset" needs to be changed to "charSet". Then you you need to make sure no browser add-ins are blocking social media trackers, otherwise you will get errors. You also need to make sure your CSP policy allows the Twitter script(s) and other content to be loaded.
Unfortunately, When it works I still see console warnings:
"Warning: Did not expect server HTML to contain a <div> in <div>."
"Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a>."
If that works for you then you are done. However, With Core Web Vitals becoming one of the biggest factors in search ranking using the classic Twitter embed iframe is not the best solution; it is slow to load and triggers Content Layout Shift (CLS) which hurts your Core Web Vitals score.
The "just paste in the embed code" approach didn't work as well as I hoped. It did not work consistently, it was slow, and I could not figure out how to make it work with Next.js dark mode dynamically.
2
Use a React component in .mdx
Enter MDX Embed which also gives you the capability of adding lots of different embeds in addition to Twitter. It's pretty awesome.
You just import the pre-built component you need and then use it in your .mdx file. The Tweet component also supports light and dark themes. Here is an example of the dark theme:
This seems to work more consistently, and if you don't have light and dark modes it's great. I could not figure out how to change the Tweet's light/dark themes dynamically, and I still had to deal with content layout shift. Hmm... what to do? Would there be a way to get the Tweet and pre-render it to eliminate CLS and make my page load faster?
3
Roll your own custom React tweet component and API
If you are a "full control" kind of person read on. First, let's check out what Lee Robinson, Director of Developer Relations at Vercel, does on his personal blog. He has a Tweets page that is really fast here. His secret is pre-rendering the tweets and then embedding them in the page. Wait, what!?
He explains all here:
There is one big drawback to using the approach Lee used - all your tweets need to be "known" ahead of time to be pre-rendered. Imagine you have a CMS and people are creating content in the CMS using Markdown/MDX and they wish to add embedded YouTube videos, Tweets, etc. to their posts. You can't really use the same approach as Lee did. Maxime Heckel figured out a way and documented it on his blog. It seemed promising, but did seem a bit "hacky" to implement.
My Plan
I decided to try a middle ground. I will create a component that renders itself on page load - it's much faster and works consistently. However I still have CLS to contend with.
Since MDX files can use/import React components let's use a custom component. To make this work we need a <CustomTweet> component that will be rendered in the browser. This component will accept the Tweet id as a prop, go get the tweet, and then render it. Like this: <CustomTweet id="abcdef123"/>
Let's create a custom Next.js API to fetch the tweet. For the component above to retrieve a tweet we can re-use Lee's code and turn it into an API. This will return the Tweet data in JSON format.
Finally we will use a tailwind styled component to render the tweet. I want this component to render a beautiful Tweet, with dynamic light/dark modes. In this case it's our <Tweet> component. This is also based off Lee's code.
Let me show you the results first. Here is my version. Here is the react-tweet version:
The first thing we want to do is create our own custom Tweet API. We will leverage Twitter's v2 REST API and return the Tweet in JSON format based on the Tweet ID. This is directly based on Lee Robinson's code. Here's more info: Twitter API v2 REST API.
The next thing we want to do is create our own custom Tweet MDX Component. It will call our API and render a Tweet. It's a pretty basic component. This is what we will to pass or import into our .mdx file:
And finally here is the Tweet component. It will take the tweet object and render it. Again, thanks to Lee Robinson's code. However, I've tweaked it a bit to improve the avatar and text styling to make it look a smidge better. Dynamic dark mode for the win! Unfortunately, this approach is super easy to use but doesn't pre-render the tweet so there is still content layout shift.