Applying How I created a Figure element component to creating a Static Text Tweet component
Social Share:
Thursday, August 25, 2022 at 4:07 PM | 5 min read
Last modified on Thursday, August 25, 2022 at 4:07 PM
#figure element, #function as prop component, #image attribution, #jsx spread attributes, #nextjs, #photographer attribution, #react, #static tweets

Photo by Camilo Jimenez on unsplash.com
Note 6.22.24: The code shared here was created for my former business site interglobalmedianetwork.com, and things were set up a bit differently there. I used the tailwind-nextjs-starter-blog template hosted on Github. You can also view my interglobalmedianetwork.com repository there.
In a previous post entitled How to Create New Lines Inside Text Prop Values in Next.js and JavaScript in General, I discuss how I came to create a static text tweet component. It was inspired by a figure (element) component I had previously created which I use for my post images where I want to give attribution to the image and the image’s photographer.
I have to say that this Figure (element) component was inspired by the Recipe Blog called “Tambouille”, created by Github handle @aloisdg (Aloïs de Gouvello), where Alois created a Figure component to give attribution to HIS recipe post images and their photographers. I teach about the figure element and using it so as to elegantly give attribution to images and their photographers, so when I saw him do that on his Recipe Blog, it caught my attention. To check out the Tambouille repository on Github, please visit aloisdg/tambouille.
It all started with the Figure component, aka "function as prop component", in which I use "props destructuring", of the props I wanted to pass to the parent Interglobalmedia component, and which returns a figure element and associated figcaption element in the returned JSX of the component. This is considered the child component. I also use the Next.js Image component for image optimization, instead of the default img element there. So:
import Image from 'next/image' import '../../styles/partials/Figure.module.scss' const Figure = ({ alt, src, srcUrl, author, authorUrl, host, hostUrl }) => { if (!alt || !src || !author || !authorUrl || !host || !hostUrl) { return null } // eslint-disable-next-line @next/next/no-img-element let image = ( <Image src={src} className="figure-image" alt={alt} width={1280} height={853} layout="intrinsic" /> ) let caption = ( <figcaption className="figcaption"> Photo by{' '} <a href={authorUrl} title={`See the profile of ${author}`}> {author} </a>{' '} on{' '} <a href={hostUrl} title={`Open ${host}`}> {host} </a> </figcaption> ) return ( <figure className="figure relative flex flex-col items-center"> {srcUrl ? ( <a className="w-full" href={srcUrl} title="See image"> {image} </a> ) : ( { image } )} {caption} </figure> ) } export default Figure
But it does not stop there. Then, I created three parent components, one called Interglobalmedia, which gives attribution to my own screenshots/images I use in my posts, one called Pexels, which gives attribution to images I use from the "Pexels" website, and one called Unsplash, which gives attribution to images I use from the "Unsplash" website.
The Interglobalmedia component:
import Figure from './Figure' const Interglobalmedia = (interglobalmediaProps) => { const props = { ...interglobalmediaProps, host: 'Interglobalmedia', hostUrl: '/', } return <Figure {...props} /> } export default Interglobalmedia
This makes for a very flexible parent component! I pass in a param called interglobalmediaProps, which I pass in and spread out with "spread syntax" in an "object literal" represented by the const variable called props. In React jargon, ...interglobalmediaProps is referred to as "JSX spread attributes". There is "spread syntax" in "vanilla JS", but JSX spread attributes, which is a JSX feature, is a syntax for passing all of an object’s properties as JSX attributes. The properties of the object I pass in are copied onto the component’s props. And then I set two "key value" pairs which I would (perhaps) expect in all Interglobalmedia component instances. The process is the same for the Pexels and Unsplash components.
If we don’t know all the properties which we want to set on a component, we might want to add them on to the object later. So in that case, we can use JSX spread attributes instead of explicitly defining which attributes we want ro pass to the component! And then, in my case in Interglobalmedia (and Pexels and Unsplash), I define only the two attributes and their values I want (perhaps) to be present in ALL my Interglobalmedia components, for example.
An example of an Interglobalmedia component I use on the site:
<Interglobalmedia src="/static/images/projects/img/webp/audio-visual-slider-app.webp" author="Maria D. Campbell" alt="Audio Visual Slider" srcUrl="/static/images/projects/img/webp/audio-visual-slider-app.webp" authorUrl="/about" />
I followed the attributes (props) I used for Pexels and Unsplash. I ignored host and hostUrl. I probably won’t ever use host and hostUrl, but I will keep those props in the parent component for now.
I had been researching how I was going to embed static tweets on here, and only came up with some very complex solutions. Packages I came across did not work, because they basically were still using JS scripts under the hood, and that meant that virtually all of the time the embedded tweet would only show up on page reload. And that makes for very bad UX.
Then I came across Maxime Heckel’s Next.js blog, where he wrote a beautiful post entitled Static Tweets with MDX and Next.js, in which he went through the steps he took to successfully create a static tweet component. But I found it to be too complex given the end goal, and he also used Typescript, which I have not yet mastered. However, the basic idea is there. Perhaps he was just a bit more “formal” about his "key value" pairs!
And one day, it dawned on me that all I really had to do was take the code and structure I created for my Figure element component and apply it to the creation of a static text tweet. I was only interested in tweets that essentially only contained text, so that is why StaticTextTweet. So I ended up doing the following:
import SocialIcon from '../social-icons' import '../../styles/partials/StaticTextTweet.module.scss' const StaticTextTweet = ({ avatar, author, name, handleUrl, handleName, text, repliesUrl, replies, retweetsUrl, retweets, likesUrl, likes, statusUrl, publishDate, twitterDeviceUrl, twitterDevice, }) => { return ( <article className="static-text-tweet-article"> <div className="static-text-tweet-div"> <blockquote className="static-text-tweet-blockquote"> <div className="static-text-tweet-header"> <a className="static-text-tweet-avatar-link" href={handleUrl} target="_blank" rel="noopener noreferrer" > <img className="static-text-tweet-avatar" src={avatar} alt={author} /> </a> <a href={handleUrl} target="_blank" rel="noopener noreferrer" className="static-text-tweet-header-author" > <span className="static-text-tweet-name"> {name} </span> <span className="static-text-tweet-handle-name text-sm"> {handleName} </span> </a> <SocialIcon className="static-text-tweet-brand-icon" kind="twitter" href={statusUrl} size="5" /> </div> <div className="static-text-tweet-text-wrapper"> <p className="static-text-tweet-text">{text}</p> </div> <div className="static-text-tweet-info"> <a href={statusUrl} target="_blank" rel="noopener noreferrer" > <b className="static-text-tweet-publish-date"> {publishDate} </b> </a> <a href={twitterDeviceUrl} target="_blank" rel="noopener noreferrer" > <b className="static-text-tweet-device"> {twitterDevice} </b> </a> </div> <div className="static-text-tweet-intents"> <a href={repliesUrl} target="_blank" rel="noopener noreferrer" className="static-text-tweet-replies-icon" > <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" > <path d="M14.046 2.242l-4.148-.01h-.002c-4.374 0-7.8 3.427-7.8 7.802 0 4.098 3.186 7.206 7.465 7.37v3.828c0 .108.045.286.12.403.143.225.385.347.633.347.138 0 .277-.038.402-.118.264-.168 6.473-4.14 8.088-5.506 1.902-1.61 3.04-3.97 3.043-6.312v-.017c-.006-4.368-3.43-7.788-7.8-7.79zm3.787 12.972c-1.134.96-4.862 3.405-6.772 4.643V16.67c0-.414-.334-.75-.75-.75h-.395c-3.66 0-6.318-2.476-6.318-5.886 0-3.534 2.768-6.302 6.3-6.302l4.147.01h.002c3.532 0 6.3 2.766 6.302 6.296-.003 1.91-.942 3.844-2.514 5.176z"></path> </svg> <b className="static-text-tweet-replies"> {replies} </b> </a> <a href={retweetsUrl} target="_blank" rel="noopener noreferrer" className="static-text-tweet-retweets-icon" > <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" > <path d="M23.77 15.67c-.292-.293-.767-.293-1.06 0l-2.22 2.22V7.65c0-2.068-1.683-3.75-3.75-3.75h-5.85c-.414 0-.75.336-.75.75s.336.75.75.75h5.85c1.24 0 2.25 1.01 2.25 2.25v10.24l-2.22-2.22c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l3.5 3.5c.145.147.337.22.53.22s.383-.072.53-.22l3.5-3.5c.294-.292.294-.767 0-1.06zm-10.66 3.28H7.26c-1.24 0-2.25-1.01-2.25-2.25V6.46l2.22 2.22c.148.147.34.22.532.22s.384-.073.53-.22c.293-.293.293-.768 0-1.06l-3.5-3.5c-.293-.294-.768-.294-1.06 0l-3.5 3.5c-.294.292-.294.767 0 1.06s.767.293 1.06 0l2.22-2.22V16.7c0 2.068 1.683 3.75 3.75 3.75h5.85c.414 0 .75-.336.75-.75s-.337-.75-.75-.75z"></path> </svg> <b className="static-text-tweet-retweets"> {retweets} </b> </a> <a className="static-text-tweet-likes-icon" href={likesUrl} target="_blank" rel="noopener noreferrer" > <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" > <path d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.813-1.148 2.353-2.73 4.644-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.375-7.454 13.11-10.037 13.156H12zM7.354 4.225c-2.08 0-3.903 1.988-3.903 4.255 0 5.74 7.035 11.596 8.55 11.658 1.52-.062 8.55-5.917 8.55-11.658 0-2.267-1.822-4.255-3.902-4.255-2.528 0-3.94 2.936-3.952 2.965-.23.562-1.156.562-1.387 0-.015-.03-1.426-2.965-3.955-2.965z"></path> </svg>{' '} <b className="static-text-tweet-likes">{likes}</b> </a> </div> </blockquote> </div> </article> ) } export default StaticTextTweet
Basically, this set the properties I was going to use via destructuring in the parent component. And then I created a parent component called TextTweetEmbed, and it looked like the following:
import StaticTextTweet from './StaticTextTweet' const TextTweetEmbed = (textTweetEmbedProps) => { const props = { ...textTweetEmbedProps, } return <StaticTextTweet {...props} /> } export default TextTweetEmbed
So all the hard work was in finding the appropriate data for the prop values, and in the styling. My styling for the static tweet was the following:
/* color variables */ $white: #fff; $twitter-blue: #3b94d9; .static-text-tweet-article { background: $white; border-radius: 0.5rem; border: 1px solid lighten(#ccc, 5%); display: block; margin: 0 auto; } .static-text-tweet-div { overflow: hidden; } .static-text-tweet-blockquote { font-style: normal; padding: 0.625rem 1.25rem 0.625rem 1.25rem; position: relative; & a.text-gray-500 { margin-left: auto; } } .static-text-tweet-header { display: flex; & a.text-gray-500 svg { fill: lighten($twitter-blue, 5%); } } .static-text-tweet-header-author { display: flex; flex-direction: column; margin-top: -0.5rem; text-decoration: none; & .static-text-tweet-name { color: #000; font-weight: bold; } & span:nth-of-type(2) { color: darken(#aaa, 15%); } } .static-text-tweet-avatar { border-radius: 50%; width: 36px; height: 36px; margin-top: 0; margin-right: 0.75rem; } .static-text-tweet-text { /* Will break line on \n and wrap text according to parent width. */ white-space: pre-wrap; } .static-text-tweet-text::before, .static-text-tweet-text::after { content: ''; } .static-text-tweet-info > a:first-of-type { margin-right: 1rem; } .static-text-tweet-info > a { text-decoration: none; } .static-text-tweet-publish-date, .static-text-tweet-device { font-weight: normal; } .static-text-tweet-intents { display: flex; justify-content: space-between; margin-top: 1.5rem; } .static-text-tweet-intents > a { text-decoration: none; & b { font-weight: normal; } } .static-text-tweet-replies-icon, .static-text-tweet-retweets-icon, .static-text-tweet-likes-icon { display: flex; }
So it should not always be about showing off how much we know about our code, but achieving the same goal with the most readable and effective code that perhaps is even simpler to create. And that is what I did here.
Happy tweeting!
Related Resources
- Function as Prop Component: react patterns
- JSX Spread Attributes: react patterns
- Recipe Blog Tambouille repository on Github: tambouille recipe blog