- Master Fullstack Development and finally ship a product
4 years ago
#Webdev
Let's not kid ourselves, you might like my writing but you totally clicked on this post mostly because there was a huge and fancy image to grab your attention.
If your blog doesn't have super huge fancy schmancy social media images yet, it's time to step up your clickbait game.
I'll show you an easy way to automatically generate them by screenshotting a React component during your build process.
Note: Yes, I know that instead of doing this at build time, you can write a serverless function that will generate these meta images at runtime, but my blog is exported as a static website and this solution works perfectly fine.
My blog posts are stored in GraphCMS, and for each post I have a metaImageUrl
field. Usually I use a random image from Unsplash, but if I don't provide an image, a default image is used as a fallback.
In your package.json
file add a generate-meta
script, and make sure to run it before your build
command.
"scripts": {"build": "yarn generate-meta && next build && next export ","generate-meta": "ts-node --project scripts/tsconfig.json scripts/generate-meta ",}
screenshot
functionWe'll use this screenshot
function to generate an image from a React component using repng.
import fs from 'fs';import path from 'path';import repng from 'repng';const screenshot = async ({props,component,options = {},fileName,outDir,}) => {//generate a picture of the componentconst file = await repng(component, { props, ...options });const finalPath = path.join(outDir, fileName);//create the needed directoriesawait fs.promises.mkdir(outDir, { recursive: true });//write the image filereturn fs.promises.writeFile(finalPath, file);};
generate-meta.js
scriptThis is the main script that's gonna be called when building your app. The generateMeta
function will fetch the posts, loop over them, and generate a screenshot for each post.
import fs from 'fs';import path from 'path';import rimraf from 'rimraf';import {client} from '../sensitive/graphql-client';import {screenshot} from './screenshot';import MetaPost from '../src/components/Meta/MetaPost';const generateMeta = async () => {//I'm using GraphQL, but you can also use REST to get your postsconst query = `query {posts {idslugtitlecontentcreatedAtbadges: typesmetaImageUrlmetaImage {url}}}`;const { posts } = await client.request(query);console.log(`✅ Got ${posts.length} posts!`);const projectPath = path.join(__dirname, "../public");const metaDir = path.join(projectPath, "meta");//delete existing screenshots (optional)rimraf.sync(metaDir);//loop over all the posts and call the screenshot function for each postfor (const post of posts) {const { slug } = post;console.log(`📸 Screenshotting ${post.type}: ${slug}...`);await screenshot({component: MetaPost,props: { post },fileName: `${slug}.png`,outDir: path.join(metaDir, post.type),});}console.log(`✅️ Done with screenshots!`);};
MetaPost
componentYou can design and style your React component however you want, just make sure to use the correct width and height and have conditional logic for longer titles.
You can check out my MetaPost
component here. I'm using inline styles with styled-mixins and flex helpers.
Here's a preview of one of my blog posts:
☝️ The Social tab in the Sizzy Debugging Panel can help you debug and preview how your meta tags will look on different social media websites (Facebook, Twitter, Google, Slack, etc.).
I'm using 1200x630
size both for Facebook and Twitter meta tags. If you want, you can also write some extra logic to generate different images for different social media websites.
Of course, somewhere in your app you would have to render the <meta>
tags and point to the correct image (based on the post slug).
I made a simple <Meta/>
React component for this:
import React from 'react';import Head from 'next/head';import { isDev } from 'utils/is-prod';const prefix = isDev ? 'http://localhost:3004' : 'https://kitze.io';const getImage = (url) => `${prefix}/${url}`;const Meta = ({title = 'Kitze',description = `Kitze's thoughts and stuff`,url = 'https://kitze.io',image,facebookImage = image,twitterImage = image}) => {return (<><Head><title>{title}</title>{/* meta */}<meta name="title" content={title} /><meta name="description" content={description} />{/*facebook */}<meta property="og:type" content="website" /><meta property="og:url" content={url} /><meta property="og:title" content={title} /><meta property="og:description" content={description} /><meta property="og:image" content={getImage(facebookImage)} />{/*twitter */}<meta property="twitter:card" content="summary_large_image" /><meta property="twitter:url" content={url} /><meta property="twitter:title" content={title} /><meta property="twitter:description" content={description} /><meta property="twitter:image" content={getImage(twitterImage)} /></Head></>);};export default Meta;
The code example is specific to Next.js, but you can easily adapt it to whatever framework you're using.
I use the Meta
component it in my post component like this:
<Metatitle={post.title}description={post.metaDescription || post.content}image={`meta/posts/${post.slug}.png`}/>
This will render the correct image based on the slug of the post.
I have seriously no idea how to conclude this post. Good luck with generating your social media images!
If you have any questions or ideas regarding this, we can discuss them during my stream 🙌