New post
All checks were successful
Build And Publish / BuildAndPublish (push) Successful in 2m42s

This commit is contained in:
Ryan Freeman 2024-10-10 22:32:49 +01:00
parent de65e69eec
commit 54fcd878f0
6 changed files with 178 additions and 11 deletions

View File

@ -17,7 +17,7 @@ const gradients = [
export async function GET(request: Request) { export async function GET(request: Request) {
const fontData = await font const fontData = await font
const {searchParams} = new URL(request.url) const {searchParams} = new URL(request.url)
const title = searchParams.get('title') const text = searchParams.get('text')
return new ImageResponse( return new ImageResponse(
( (
@ -44,7 +44,7 @@ export async function GET(request: Request) {
'-webkit-background-clip': 'text', '-webkit-background-clip': 'text',
color: 'transparent', color: 'transparent',
}}> }}>
{title} {text}
</div> </div>
</div> </div>
), ),
@ -59,5 +59,5 @@ export async function GET(request: Request) {
} }
] ]
} }
); )
} }

View File

@ -24,13 +24,14 @@ export const metadata = {
description: meta.description, description: meta.description,
images: [ images: [
{ {
url: `/api/og-image?title=${meta.heading}`, url: `/api/og-image?text=${meta.heading}`,
width: 1200, width: 1200,
height: 600, height: 600,
alt: meta.heading, alt: meta.heading,
type: 'image/png' type: 'image/png'
} }
] ],
type: 'website'
} }
} }
@ -98,7 +99,7 @@ export default function Services() {
return ( return (
<SimpleLayout <SimpleLayout
heading="I offer a wide range of digital services to elevate and transform your business" heading={metadata.heading}
description={metadata.description} description={metadata.description}
gradient="bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400"> gradient="bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400">
<ul <ul
@ -117,4 +118,4 @@ export default function Services() {
</ul> </ul>
</SimpleLayout> </SimpleLayout>
) )
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -0,0 +1,147 @@
import {ArticleLayout} from '../../../components/layouts/ArticleLayout'
import {createSlug} from '../../../lib/createSlug'
import Image from 'next/image'
import ogMetaTags from './open-graph-meta-tags.png'
export const meta = {
authors: 'Ryan Freeman',
title: 'Generating dynamic Open Graph images with Next.js',
date: '2024-10-10',
description: 'In this post I\'ll talk about how I created dynamic, eye-catching Open Graph images with Next.js for this website.'
}
export const metadata = {
...meta,
openGraph: {
title: meta.title,
description: meta.description,
images: [
{
url: `/api/og-image?text=${meta.title}`,
width: 1200,
height: 600,
alt: meta.title,
type: 'image/png'
}
],
type: 'website'
}
}
export default (props) => <ArticleLayout
title={metadata.title}
date={metadata.date}
description={metadata.description}
slug={createSlug(metadata.title)}
{...props} />
<Image src={`/api/og-image?text=10 Surprising Benefits of Talking to Yourself in Public`} width="1200" height="600" alt=""/>
In this post I\'ll talk about how I created dynamic, eye-catching Open Graph images with Next.js for this website.
## What is Open Graph?
[Open Graph](https://ogp.me/) is the web standard that allows website owners to manage the appearance of their content when it is shared on social media platforms.
In other words, it enables website owners to attract readers to their content, similar to how thumbnails on YouTube videos attract our attention and entice us to click on a video.
## Show me the code
Alright, let\'s see how this was achieved in code. Taking the new [Services page](/services) as an example, the first step was to create a new API route located at `/api/og-image`.
### API route
This API route should allow me to specify some text like a page heading and dynamically generate an image, which I can then reference using the Open Graph meta tags on each page.
```javascript
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const text = searchParams.get('text')
```
This was straightforward to implement, here the API route responds to a `GET` request and accepts a `text` parameter in the url. For example, `/api/og-image?text=hello,world`.
```javascript
return new ImageResponse(
(
<div
style={{
fontSize: 64,
background: 'black',
width: '100%',
height: '100%',
display: 'flex',
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center',
lineHeight: '1',
padding: '0 128px'
}}
>
<div
style={{
backgroundImage: 'radial-gradient(at right top, rgb(221, 214, 254), rgb(239, 68, 68), rgb(251, 146, 60))',
backgroundClip: 'text',
// @ts-ignore
'-webkit-background-clip': 'text',
color: 'transparent'
}}>
{text}
</div>
</div>
),
{
width: 1200,
height: 600
}
)
```
Next, I thought about how I wanted the Open Graph image to look. To keep things simple, I settled on a black background with the text centered both horizontally and vertically.
This was achieved using the [ImageReponse](https://nextjs.org/docs/app/api-reference/functions/image-response) constructor, which allows you to create and style dynamic images using JSX and CSS.
<Image src={`/api/og-image?text=hello,world`} width="1200" height="600" alt=""/>
When you visit the API route and specify some text in the url, you'll get something that looks like the image above. Next, I'll demonstrate how I added the Open Graph meta tags to the Services page to render this dynamic image for social media platforms.
### Open Graph Meta Tags
The last step was to include the Open Graph meta tags on the Services page so that when it's shared, an eye-catching image will appear to draw readers to the content.
```javascript
const meta = {
title: 'Services - Ryan Freeman',
heading: 'I offer a wide range of digital services to elevate and transform your business',
description: 'Whether you need a WordPress website, React app, AWS support or odd coding jobs, I\'m here to help. ' +
'As an experienced software engineer, I produce high-quality software that will deliver immediate value for you and your customers.',
}
export const metadata = {
...meta,
openGraph: {
title: meta.title,
description: meta.description,
images: [
{
url: `/api/og-image?text=${meta.heading}`,
width: 1200,
height: 600,
alt: meta.heading,
type: 'image/png'
}
],
type: 'website'
}
}
```
By exporting the `metadata` object, I was able to specify the Open Graph meta tags for this page. In this example, you can see how Im fetching data from the new API route, which generates a dynamic image featuring the page heading, sized at 1200 by 600 pixels.
<Image src={ogMetaTags} alt=""/>
Lastly, I can verify everything is working correctly by loading the page in the browser.
## Conclusion
In this post, I demonstrated how you can create dynamic Open Graph images with Next.js for creating content previews which engage readers and attract them to your content.

View File

@ -6,11 +6,30 @@ import {formatDate} from '@/lib/formatDate'
import {getAllArticles} from '@/lib/getAllArticles' import {getAllArticles} from '@/lib/getAllArticles'
import type {Article} from '@/types' import type {Article} from '@/types'
export const metadata = { const meta = {
title: 'Writing - Ryan Freeman', title: 'Writing - Ryan Freeman',
heading: 'Writing on software engineering, and everything in between.',
description: 'All of my long-form thoughts on software engineering, and more, displayed in chronological order.' description: 'All of my long-form thoughts on software engineering, and more, displayed in chronological order.'
} }
export const metadata = {
...meta,
openGraph: {
title: meta.title,
description: meta.description,
images: [
{
url: `/api/og-image?text=${meta.heading}`,
width: 1200,
height: 600,
alt: meta.heading,
type: 'image/png'
}
],
type: 'website'
}
}
function Article({article}: { article: Article }) { function Article({article}: { article: Article }) {
return ( return (
<article> <article>
@ -38,7 +57,7 @@ export default async function Writing() {
return ( return (
<SimpleLayout <SimpleLayout
heading="Writing on software engineering, and everything in between." heading={metadata.heading}
description={metadata.description} description={metadata.description}
gradient="bg-gradient-to-r from-pink-500 to-violet-500" gradient="bg-gradient-to-r from-pink-500 to-violet-500"
> >

View File

@ -1,5 +1,5 @@
export function createSlug(title: string) { export function createSlug(title: string) {
return title.toLowerCase() return title.toLowerCase()
.replace(/['?:]+/g, '') .replace(/['?:.,]+/g, '')
.replace(/[.,\s]+/g, '-') .replace(/\s+/g, '-')
} }