mirror of
https://github.com/r-freeman/portfolio.git
synced 2024-11-13 17:05:42 +00:00
This commit is contained in:
parent
de65e69eec
commit
18161a50e8
@ -17,7 +17,7 @@ const gradients = [
|
||||
export async function GET(request: Request) {
|
||||
const fontData = await font
|
||||
const {searchParams} = new URL(request.url)
|
||||
const title = searchParams.get('title')
|
||||
const text = searchParams.get('text')
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
@ -44,7 +44,7 @@ export async function GET(request: Request) {
|
||||
'-webkit-background-clip': 'text',
|
||||
color: 'transparent',
|
||||
}}>
|
||||
{title}
|
||||
{text}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
@ -59,5 +59,5 @@ export async function GET(request: Request) {
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
)
|
||||
}
|
@ -24,13 +24,14 @@ export const metadata = {
|
||||
description: meta.description,
|
||||
images: [
|
||||
{
|
||||
url: `/api/og-image?title=${meta.heading}`,
|
||||
url: `/api/og-image?text=${meta.heading}`,
|
||||
width: 1200,
|
||||
height: 600,
|
||||
alt: meta.heading,
|
||||
type: 'image/png'
|
||||
}
|
||||
]
|
||||
],
|
||||
type: 'website'
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +99,7 @@ export default function Services() {
|
||||
|
||||
return (
|
||||
<SimpleLayout
|
||||
heading="I offer a wide range of digital services to elevate and transform your business"
|
||||
heading={metadata.heading}
|
||||
description={metadata.description}
|
||||
gradient="bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400">
|
||||
<ul
|
||||
@ -117,4 +118,4 @@ export default function Services() {
|
||||
</ul>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
@ -0,0 +1,145 @@
|
||||
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} />
|
||||
|
||||
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 I’m 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.
|
@ -6,11 +6,30 @@ import {formatDate} from '@/lib/formatDate'
|
||||
import {getAllArticles} from '@/lib/getAllArticles'
|
||||
import type {Article} from '@/types'
|
||||
|
||||
export const metadata = {
|
||||
const meta = {
|
||||
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.'
|
||||
}
|
||||
|
||||
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 }) {
|
||||
return (
|
||||
<article>
|
||||
@ -38,7 +57,7 @@ export default async function Writing() {
|
||||
|
||||
return (
|
||||
<SimpleLayout
|
||||
heading="Writing on software engineering, and everything in between."
|
||||
heading={metadata.heading}
|
||||
description={metadata.description}
|
||||
gradient="bg-gradient-to-r from-pink-500 to-violet-500"
|
||||
>
|
||||
|
@ -1,5 +1,5 @@
|
||||
export function createSlug(title: string) {
|
||||
return title.toLowerCase()
|
||||
.replace(/['?:]+/g, '')
|
||||
.replace(/[.,\s]+/g, '-')
|
||||
.replace(/['?:.,]+/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
}
|
Loading…
Reference in New Issue
Block a user