Generate Dynamic Open Graph with Next.js 14
One powerful way to enhance your website's shareability on social media platforms is by implementing dynamic Open Graph meta tags. In this comprehensive guide, we'll delve into how you can seamlessly achieve this with Next.js 14, empowering your site to stand out in the crowded digital space.
Exciting Times for Next.js Enthusiasts
Exciting times have arrived for Next.js enthusiasts! The release of Next.js 13 brought stability to the App Router, accompanied by a wave of innovative features and approaches. As developers, staying on the cutting edge is paramount, and this release propels us into a realm of possibilities.
In this series of articles, we explore the new horizons that Next.js 13 unfolds. Today, our focus is on a crucial aspect of web development — SEO. With the introduction of Next.js 13, we bid farewell to the old 'Head.tsx' way of managing SEO. Instead, we embark on a journey to discover a fresh approach that aligns with the latest practices and elevates the way we optimize our websites.
Introduction: Understanding Open Graph
Before we dive into the implementation details, let's understand the significance of Open Graph. Developed by Facebook, Open Graph is an internet protocol that standardizes the incorporation of metadata on a webpage to represent its content. It enables the inclusion of information ranging from the page title to specific details like the duration of a video. Have you ever posted a link on social media?
Implementing Dynamic Open Graph with Next.js
Let's take a practical approach and see how we can implement dynamic Open Graph in Next.js. The following code snippet demonstrates how to generate dynamic Open Graph images based on a post title.
Step 1: Create a new Next.js project
Start by creating a new Next.js project. Open your terminal and run the following command:
bunx create-next-app@latest
bunx create-next-app@latest
Here's how the terminal output should look like:
// Terminal output
✔ What is your project named? … dynamic-opengraph-demo
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
✔ What import alias would you like configured? … @/*
// Terminal output
✔ What is your project named? … dynamic-opengraph-demo
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
✔ What import alias would you like configured? … @/*
And then change directory to the newly created project:
cd dynamic-opengraph-demo
cd dynamic-opengraph-demo
Here's the project structure:
.
├── README.md
├── bun.lockb
├── next-env.d.js
├── next.config.js
├── package.json
├── postcss.config.js
├── public
│ ├── fonts // I created this folder to store my fonts
│ │ └── outfit-semibold.ttf // Here am using outfit font
│ ├── og-bg.png // I created this image to use as background
│ ├── next.svg
│ └── vercel.svg
├── src
│ └── app
│ ├── api // I created this folder to store my api routes
│ │ └── og
│ │ └── route.tsx // Here is my og route
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── tailwind.config.js
└── tsconfig.json
.
├── README.md
├── bun.lockb
├── next-env.d.js
├── next.config.js
├── package.json
├── postcss.config.js
├── public
│ ├── fonts // I created this folder to store my fonts
│ │ └── outfit-semibold.ttf // Here am using outfit font
│ ├── og-bg.png // I created this image to use as background
│ ├── next.svg
│ └── vercel.svg
├── src
│ └── app
│ ├── api // I created this folder to store my api routes
│ │ └── og
│ │ └── route.tsx // Here is my og route
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── tailwind.config.js
└── tsconfig.json
As you can see, I created a folder called api to store my api routes. I also created a folder called fonts to store my fonts. You can use any font you want.
Step 2: Implement the Dynamic Open Graph Route
Now, let's delve into the code. Here's how you can implement the dynamic Open Graph route in your Next.js project:
// Import required modules and constants
import { ImageResponse } from "next/og";
import { NextRequest } from "next/server";
// Route segment config
export const runtime = "edge";
// Define a function to handle GET requests
export async function GET(req: NextRequest) {
// Extract title from query parameters
const { searchParams } = req.nextUrl;
const postTitle = searchParams.get("title");
// Fetch the Outfit font from the specified URL
const font = fetch(
new URL("../../../../public/fonts/outfit-semibold.ttf", import.meta.url),
).then((res) => res.arrayBuffer());
const fontData = await font;
// Create an ImageResponse with dynamic content
return new ImageResponse(
(
<div
style={{
height: "100%",
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
justifyContent: "center",
backgroundImage: `url(http://localhost:3000/og-bg.png)`,
}}
>
<div
style={{
marginLeft: 190,
marginRight: 190,
display: "flex",
fontSize: 140,
fontFamily: "Outfit",
letterSpacing: "-0.05em",
fontStyle: "normal",
color: "white",
lineHeight: "120px",
whiteSpace: "pre-wrap",
}}
>
{postTitle}
</div>
</div>
),
// ImageResponse options
{
width: 1920,
height: 1080,
fonts: [
{
name: "Outfit",
data: fontData,
style: "normal",
},
],
},
);
}
// Import required modules and constants
import { ImageResponse } from "next/og";
import { NextRequest } from "next/server";
// Route segment config
export const runtime = "edge";
// Define a function to handle GET requests
export async function GET(req: NextRequest) {
// Extract title from query parameters
const { searchParams } = req.nextUrl;
const postTitle = searchParams.get("title");
// Fetch the Outfit font from the specified URL
const font = fetch(
new URL("../../../../public/fonts/outfit-semibold.ttf", import.meta.url),
).then((res) => res.arrayBuffer());
const fontData = await font;
// Create an ImageResponse with dynamic content
return new ImageResponse(
(
<div
style={{
height: "100%",
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
justifyContent: "center",
backgroundImage: `url(http://localhost:3000/og-bg.png)`,
}}
>
<div
style={{
marginLeft: 190,
marginRight: 190,
display: "flex",
fontSize: 140,
fontFamily: "Outfit",
letterSpacing: "-0.05em",
fontStyle: "normal",
color: "white",
lineHeight: "120px",
whiteSpace: "pre-wrap",
}}
>
{postTitle}
</div>
</div>
),
// ImageResponse options
{
width: 1920,
height: 1080,
fonts: [
{
name: "Outfit",
data: fontData,
style: "normal",
},
],
},
);
}
Code Explanation
- Import Modules and Constants: Import the necessary modules and constants.
- Set Runtime: Declare runtime as 'edge'.
- Handle GET Requests: Define an asynchronous function to handle GET requests.
- Fetch Font Data: Use the fetch API to get the Outfit font data and convert it to an array buffer.
- Create ImageResponse: Generate an ImageResponse object with dynamic JSX content.
- Set ImageResponse Options: Specify options for the ImageResponse, including width, height, and font details.
Step 3: Test Your Dynamic Open Graph Route
Now, let's test your dynamic Open Graph route. Follow these steps:
- Start your Next.js development server with bun run dev
- Open your browser and navigate to http://localhost:3000/api/og?title=YourDynamicTitle. Replace "YourDynamicTitle" with the desired title for your Open Graph image.
Step 4: Implement the Dynamic Open Graph Meta Tags
Next14 uses an object called metadata to describe the page’s SEO attributes including OG attributes. (can be placed in layout.js / page.js)
import { Metadata } from 'next'; // if using TypeScript
export const metadata: Metadata = {
openGraph: {
title: 'Next.js',
description: 'The React Framework for the Web',
url: 'https://nextjs.org',
siteName: 'Next.js',
images: [
{
url: 'http://localhost:3000/api/og?title=Next.js', // Dynamic og route
width: 800,
height: 600,
},
{
url: 'http://localhost:3000/api/og?title=Next.js', // Dynamic og route
width: 1800,
height: 1600,
alt: 'My custom alt',
},
],
locale: 'en_US',
type: 'website',
},
};
export default function Page() {}
import { Metadata } from 'next'; // if using TypeScript
export const metadata: Metadata = {
openGraph: {
title: 'Next.js',
description: 'The React Framework for the Web',
url: 'https://nextjs.org',
siteName: 'Next.js',
images: [
{
url: 'http://localhost:3000/api/og?title=Next.js', // Dynamic og route
width: 800,
height: 600,
},
{
url: 'http://localhost:3000/api/og?title=Next.js', // Dynamic og route
width: 1800,
height: 1600,
alt: 'My custom alt',
},
],
locale: 'en_US',
type: 'website',
},
};
export default function Page() {}
the above code will generate the following meta tags:
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image:url" content="http://localhost:3000/api/og?title=Next.js" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image:url" content="http://localhost:3000/api/og?title=Next.js" />
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="My custom alt" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image:url" content="http://localhost:3000/api/og?title=Next.js" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image:url" content="http://localhost:3000/api/og?title=Next.js" />
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="My custom alt" />
<meta property="og:type" content="website" />
Conclusion
With these steps, you've successfully implemented and tested dynamic Open Graph in your Next.js project. This feature enhances your website's shareability on social media platforms and provides a visually appealing representation of your content.
For more information, check out the official documentation on Open Graph or check out on my Github.