diff --git a/app/api/og-image/route.tsx b/app/api/og-image/route.tsx
index 1bd4cbe..4aa1d1d 100644
--- a/app/api/og-image/route.tsx
+++ b/app/api/og-image/route.tsx
@@ -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}
),
@@ -59,5 +59,5 @@ export async function GET(request: Request) {
}
]
}
- );
+ )
}
\ No newline at end of file
diff --git a/app/services/page.tsx b/app/services/page.tsx
index 3edba97..4169524 100644
--- a/app/services/page.tsx
+++ b/app/services/page.tsx
@@ -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 (
)
-}
+}
\ No newline at end of file
diff --git a/app/writing/generating-dynamic-open-graph-images-with-nextjs/open-graph-meta-tags.png b/app/writing/generating-dynamic-open-graph-images-with-nextjs/open-graph-meta-tags.png
new file mode 100644
index 0000000..65dddf5
Binary files /dev/null and b/app/writing/generating-dynamic-open-graph-images-with-nextjs/open-graph-meta-tags.png differ
diff --git a/app/writing/generating-dynamic-open-graph-images-with-nextjs/page.mdx b/app/writing/generating-dynamic-open-graph-images-with-nextjs/page.mdx
new file mode 100644
index 0000000..fd45941
--- /dev/null
+++ b/app/writing/generating-dynamic-open-graph-images-with-nextjs/page.mdx
@@ -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) =>
+
+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(
+ (
+
+ ),
+ {
+ 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.
+
+
+
+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.
+
+
+
+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.
\ No newline at end of file
diff --git a/app/writing/page.tsx b/app/writing/page.tsx
index 332d2f4..66d163b 100644
--- a/app/writing/page.tsx
+++ b/app/writing/page.tsx
@@ -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 (
@@ -38,7 +57,7 @@ export default async function Writing() {
return (
diff --git a/lib/createSlug.ts b/lib/createSlug.ts
index db2776f..148f3ca 100644
--- a/lib/createSlug.ts
+++ b/lib/createSlug.ts
@@ -1,5 +1,5 @@
export function createSlug(title: string) {
return title.toLowerCase()
- .replace(/['?:]+/g, '')
- .replace(/[.,\s]+/g, '-')
+ .replace(/['?:.,]+/g, '')
+ .replace(/\s+/g, '-')
}
\ No newline at end of file