mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-04 10:07:26 +02:00
💄 Custom og-image for blog posts (#814)
This commit is contained in:
parent
a9e41b2f0d
commit
c24af3d808
7 changed files with 253 additions and 11 deletions
|
@ -20,6 +20,7 @@
|
|||
"@svgr/webpack": "^6.5.1",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@vercel/analytics": "^0.1.8",
|
||||
"@vercel/og": "^0.5.11",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"class-variance-authority": "^0.6.0",
|
||||
"dayjs": "^1.11.7",
|
||||
|
@ -31,7 +32,7 @@
|
|||
"lodash": "^4.17.21",
|
||||
"nanoid": "^4.0.0",
|
||||
"next-i18next": "^13.0.3",
|
||||
"next-seo": "^5.15.0",
|
||||
"next-seo": "^6.1.0",
|
||||
"react-i18next": "^12.1.4",
|
||||
"react-use": "^17.4.0",
|
||||
"remark": "^14.0.3",
|
||||
|
@ -39,7 +40,6 @@
|
|||
"typescript": "^4.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"@next/bundle-analyzer": "^12.3.4",
|
||||
"@rallly/tsconfig": "*",
|
||||
"@types/accept-language-parser": "^1.5.3",
|
||||
|
@ -53,6 +53,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-config-next": "^13.0.1",
|
||||
"eslint-config-turbo": "^0.0.9",
|
||||
|
|
BIN
apps/landing/public/static/fonts/inter-bold.ttf
Normal file
BIN
apps/landing/public/static/fonts/inter-bold.ttf
Normal file
Binary file not shown.
BIN
apps/landing/public/static/fonts/inter-regular.ttf
Normal file
BIN
apps/landing/public/static/fonts/inter-regular.ttf
Normal file
Binary file not shown.
80
apps/landing/src/pages/api/og-image.tsx
Normal file
80
apps/landing/src/pages/api/og-image.tsx
Normal file
|
@ -0,0 +1,80 @@
|
|||
/* eslint-disable @next/next/no-img-element */
|
||||
import { ImageResponse } from "@vercel/og";
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
export const config = {
|
||||
runtime: "edge",
|
||||
};
|
||||
|
||||
const regularFont = fetch(
|
||||
new URL("/public/static/fonts/inter-regular.ttf", import.meta.url),
|
||||
).then((res) => res.arrayBuffer());
|
||||
|
||||
const boldFont = fetch(
|
||||
new URL("/public/static/fonts/inter-bold.ttf", import.meta.url),
|
||||
).then((res) => res.arrayBuffer());
|
||||
|
||||
export default async function handler(req: NextRequest) {
|
||||
const [regularFontData, boldFontData] = await Promise.all([
|
||||
regularFont,
|
||||
boldFont,
|
||||
]);
|
||||
|
||||
const { searchParams } = req.nextUrl;
|
||||
const title = searchParams.get("title");
|
||||
const excerpt = searchParams.get("excerpt");
|
||||
const type = searchParams.get("type");
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
<div tw="bg-gray-50 h-full flex-col w-full px-18 py-16 flex">
|
||||
<div tw="mb-16 flex justify-between">
|
||||
<div tw="flex justify-between items-center w-full">
|
||||
<img
|
||||
alt="Rallly"
|
||||
src="https://rallly.co/logo-color.svg"
|
||||
height={64}
|
||||
/>
|
||||
<div tw="flex text-gray-800 text-3xl tracking-tight font-bold">
|
||||
<span tw="bg-gray-200 px-6 py-3 rounded-full">{type}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 tw="flex flex-col text-7xl leading-tight font-bold tracking-tight text-gray-900 text-left">
|
||||
<span>{title}</span>
|
||||
</h2>
|
||||
{excerpt ? (
|
||||
<p tw="text-4xl leading-relaxed text-gray-500">{excerpt}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{
|
||||
name: "Inter",
|
||||
data: regularFontData,
|
||||
weight: 400,
|
||||
},
|
||||
{
|
||||
name: "Inter",
|
||||
data: boldFontData,
|
||||
weight: 700,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
import { ArrowLeftIcon } from "@rallly/icons";
|
||||
import { absoluteUrl } from "@rallly/utils";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import ErrorPage from "next/error";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { NextSeo } from "next-seo";
|
||||
|
||||
import PostBody from "@/components/blog/post-body";
|
||||
import PostHeader from "@/components/blog/post-header";
|
||||
|
@ -28,6 +30,29 @@ const Page: NextPageWithLayout<Props> = ({ post }) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<NextSeo
|
||||
openGraph={{
|
||||
images: [
|
||||
{
|
||||
url:
|
||||
`${absoluteUrl()}/_next/image?w=1200&q=100&url=${encodeURIComponent(
|
||||
`/api/og-image`,
|
||||
)}` +
|
||||
encodeURIComponent(
|
||||
`?type=${encodeURIComponent(
|
||||
"Blog",
|
||||
)}&title=${encodeURIComponent(
|
||||
post.title,
|
||||
)}&excerpt=${encodeURIComponent(post.excerpt)}`,
|
||||
),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: post.title,
|
||||
type: "image/png",
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<nav className="mb-2">
|
||||
<Link
|
||||
className="text-muted-foreground hover:text-primary inline-flex items-center gap-x-2 text-sm font-medium"
|
||||
|
@ -39,7 +64,6 @@ const Page: NextPageWithLayout<Props> = ({ post }) => {
|
|||
<article>
|
||||
<Head>
|
||||
<title>{post.title}</title>
|
||||
<meta property="og:image" content={post.ogImage?.url} />
|
||||
</Head>
|
||||
<PostHeader title={post.title} date={post.date} />
|
||||
<PostBody content={post.content} />
|
||||
|
@ -79,9 +103,8 @@ export async function getStaticProps(ctx: GetStaticPropsContext) {
|
|||
"date",
|
||||
"slug",
|
||||
"author",
|
||||
"excerpt",
|
||||
"content",
|
||||
"ogImage",
|
||||
"coverImage",
|
||||
]);
|
||||
const content = await markdownToHtml(post.content || "");
|
||||
|
||||
|
|
|
@ -25,9 +25,6 @@ export type Post = {
|
|||
title: string;
|
||||
date: string;
|
||||
coverImage?: string;
|
||||
excerpt?: string;
|
||||
ogImage?: {
|
||||
url: string;
|
||||
};
|
||||
excerpt: string;
|
||||
content: string;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue