22
loading...
This website collects cookies to deliver better user experience
// path: /pages/api/open-graph-image.ts
import type { NextApiRequest, NextApiResponse } from "next";
import chromium from 'chrome-aws-lambda';
import { chromium as playwrightChromium } from 'playwright-core';
// getAbsoluteURL is in a snippet further down
import { getAbsoluteURL } from 'utils/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// Start the browser with the AWS Lambda wrapper (chrome-aws-lambda)
const browser = await playwrightChromium.launch({
args: chromium.args,
executablePath: await chromium.executablePath,
headless: chromium.headless,
})
// Create a page with the Open Graph image size best practise
// 1200x630 is a good size for most social media sites
const page = await browser.newPage({
viewport: {
width: 1200,
height: 630
}
});
// Generate the full URL out of the given path (GET parameter)
const relativeUrl = (req.query["path"] as string) || "";
const url = getAbsoluteURL(relativeUrl)
await page.goto(url, {
timeout: 15 * 1000,
// waitUntil option will make sure everything is loaded on the page
waitUntil: "networkidle"
})
const data = await page.screenshot({
type: "png"
})
await browser.close()
// Set the s-maxage property which caches the images then on the Vercel edge
res.setHeader("Cache-Control", "s-maxage=31536000, stale-while-revalidate")
res.setHeader('Content-Type', 'image/png')
// write the image to the response with the specified Content-Type
res.end(data)
}
// Gets the URL for the current environment
export const getAbsoluteURL = (path: string) => {
const baseURL = process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : "http://localhost:3000"
return baseURL + path
}
import { useRouter } from "next/router";
import { getAbsoluteURL } from "./utils";
export default function useOpenGraphImage() {
const router = useRouter();
const searchParams = new URLSearchParams();
// The [slug] from /posts/[slug] and /posts/open-graph/[slug]
// should be identical.
searchParams.set(
"path",
router.asPath.replace("/posts/", "/posts/open-graph/")
);
// Open Graph & Twitter images need a full URL including domain
const fullImageURL = getAbsoluteURL(`/api/open-graph-image?${searchParams}`);
return { imageURL: fullImageURL };
}
import Head from "next/head";
const postComponent = (props) => {
const { imageURL } = useOpenGraphImage(); // <- This custom hook here!
return <>
<Head>
<title>Kacey Cleveland - {title}</title>
<meta name="description" content={props.description} />
<meta property="og:title" content={props.title} />
<meta property="og:type" content="article" />
<meta property="og:image" content={imageURL} />
</Head>
<div>
// Content here
</div>
</>;
}
import { useRouter } from "next/router";
import { getAbsoluteURL } from "./utils";
export default function useOpenGraphImage() {
const router = useRouter();
const searchParams = new URLSearchParams();
searchParams.set(
"path",
router.asPath.replace("/posts/", "/posts/open-graph/") // This will take the current URL of the post and give us the open-graph one. Modify as needed for how you have your routing setup
);
const fullImageURL = getAbsoluteURL(`/api/open-graph-image?${searchParams}`); // This will then pass along the route for the open-graph image to our api request which will run the serverless function which runs headless chrome and goes to the /posts-open-graph/[slug].tsx route and takes a screenshot to serve as the 'fullImageURL' return.
return { imageURL: fullImageURL };
}