- Published on
Applying how I created a Figure element component to creating a Static Text Tweet component
- Authors
- Name
- interglobalmedia
- @letsbsocial1
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 Pexels
, and one called Unsplash
, which gives attribution to images
I use from Unsplash
. 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 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 in 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