Vercel OG Images with DatoCMS
Hi! This quick tutorial on how I implemented Vercel OG Images for the future React Miami speaker cards. (At the time of writing this, these pages are still in dev until our full lineup is announced in January 2023.)
Exactly ONE single person asked how I did this when I shared this on Twitter (s/o @ shiva_baaba) but figured it may be helpful for others so why not do a whole thing on Hashnode about it?
My project is a fork of Vercel's Virtual Event Starter Kit, therefore that is the context I'll be using to step through this guide.
First Things First
Fork the Vercel Virtual Event Starter Kit and deploy with DatoCMS: https://vercel.com/templates/next.js/virtual-event-starter-kit
Check out the docs on Vercel's OG Images and follow the installation instructions here: https://vercel.com/docs/concepts/functions/edge-functions/og-image-generation
In Your DatoCMS Project
In the Settings tab of your project, add a new field to your Speaker model called Social Image.
Now when you add a Speaker you will have a new field where you can upload your custom image. I created my custom images with Canva, but you can use whatever tool you prefer!
In Your Code Repository
Create or update the following files to implement OG images for your project from Dato sent with the Speaker information.
Create file: pages/api/og.tsx
This will handle the OG image generation and is modeled after the examples from Vercel's documentation.
Note: If a page doesn't have a custom social image, then it will default to our project's twitter-card.
import { ImageResponse } from '@vercel/og';
import { NextRequest } from 'next/server';
import { SITE_URL } from '@lib/constants';
export const config = {
runtime: 'experimental-edge',
};
export default function handler(req: NextRequest) {
const { searchParams } = req.nextUrl;
const socialImageUrl = searchParams.get('entity');
if (!socialImageUrl) {
return new ImageResponse(
(
<div
style={{
display: 'flex',
width: '100%',
height: '100%',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img
width="1200"
height="630"
src={`${SITE_URL}twitter-card.png`}
/>
</div>
),
{
width: 1200,
height: 630,
},
);
}
return new ImageResponse(
(
<div
style={{
display: 'flex',
width: '100%',
height: '100%',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img
width="1200"
height="630"
src={socialImageUrl}
/>
</div>
),
{
width: 1200,
height: 630,
},
);
}
Update file: lib/dato.ts
Update the schema for getAllSpeakers()
to include socialImage
.
Update the url
property's width (w
) and height (h
) with that of the OG image.
We don't need the blurDataURL
property here because that's used for the loading state of our web pages and doesn't apply to our social cards.
export async function getAllSpeakers(): Promise<Speaker[]> {
const data = await fetchCmsAPI(`
{
allSpeakers(first: 100) {
name
bio
title
slug
twitter
github
company
talk {
title
description
}
image {
url(imgixParams: {fm: jpg, fit: crop, w: 300, h: 400})
blurDataURL: blurUpThumb
}
imageSquare: image {
url(imgixParams: {fm: jpg, fit: crop, w: 192, h: 192})
blurDataURL: blurUpThumb
}
socialImage {
url(imgixParams: {fm: jpg, fit: crop, w: 1200, h: 630})
}
}
}
`);
return data.allSpeakers;
}
Update file: lib/types.ts
Update type Speaker
to include socialImage
and set the type to Image
.
export type Speaker = {
name: string;
bio: string;
title: string;
slug: string;
twitter: string;
github: string;
company: string;
talk: Talk;
image: Image;
imageSquare: Image;
socialImage: Image;
};
Update file: pages/speakers/[slug].tsx
Import the SITE_URL
variable from @lib/constants
.
Update the meta
object to include the image
property that is available via the Meta
type in /components/page.tsx
.
Set the value of image
to the og api we created earlier and add a parameter called entity
.
Set the value of the entity
parameter to the social image we have included with our Speaker information.
import { SITE_URL } from '@lib/constants';
const meta = {
title: 'Demo - Virtual Event Starter Kit',
description: META_DESCRIPTION
image: `${SITE_URL}api/og?entity=${speaker?.socialImage?.url}`
};
Et Violà!
This is how it looks when it's all done!
This page has a social image and you can see it here:
This page does not have a social image so you can see our twitter-card default here:
All in all, even though Vercel's OG Images are brand new, I believe they're going to quickly catch fire and be used everywhere. I recommend trying them out with one of your projects and of course, reading the docs from their team first!
Hope this helps! If you have any questions, leave them in the replies below!
Subscribe to my newsletter
Read articles from Michelle Bakels directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by