mirror of
https://github.com/r-freeman/portfolio.git
synced 2024-11-21 21:05:41 +00:00
This commit is contained in:
parent
de65e69eec
commit
b2c38bddb6
@ -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) {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -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 |
@ -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 {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"
|
||||||
>
|
>
|
||||||
|
@ -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, '-')
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user