diff --git a/apps/web/components/seo/head-seo.tsx b/apps/web/components/seo/head-seo.tsx
index d0756ddb09..3495d4e731 100644
--- a/apps/web/components/seo/head-seo.tsx
+++ b/apps/web/components/seo/head-seo.tsx
@@ -4,6 +4,7 @@ import { NextSeo, NextSeoProps } from "next-seo";
import {
AppImageProps,
constructAppImage,
+ constructGenericImage,
constructMeetingImage,
MeetingImageProps,
} from "@calcom/lib/OgImages";
@@ -72,12 +73,11 @@ const buildSeoMeta = (pageProps: {
export const HeadSeo = (props: HeadSeoProps): JSX.Element => {
const defaultUrl = getBrowserInfo()?.url;
- const image = getSeoImage("default");
const { title, description, siteName, canonical = defaultUrl, nextSeoProps = {}, app, meeting } = props;
- const truncatedDescription = truncate(description, 24);
- const longerTruncatedDescriptionOnWords = truncateOnWord(description, 148);
+ const image = getSeoImage("ogImage") + constructGenericImage({ title, description });
+ const truncatedDescription = truncateOnWord(description, 158);
const pageTitle = title + " | Cal.com";
let seoObject = buildSeoMeta({
@@ -101,7 +101,7 @@ export const HeadSeo = (props: HeadSeoProps): JSX.Element => {
if (app) {
const pageImage =
- getSeoImage("ogImage") + constructAppImage({ ...app, description: longerTruncatedDescriptionOnWords });
+ getSeoImage("ogImage") + constructAppImage({ ...app, description: truncatedDescription });
seoObject = buildSeoMeta({
title: pageTitle,
description: truncatedDescription,
diff --git a/apps/web/pages/api/social/og/image.tsx b/apps/web/pages/api/social/og/image.tsx
index 8445f4de40..fd8295d1dd 100644
--- a/apps/web/pages/api/social/og/image.tsx
+++ b/apps/web/pages/api/social/og/image.tsx
@@ -3,7 +3,7 @@ import { NextApiRequest } from "next";
import type { SatoriOptions } from "satori";
import { z } from "zod";
-import { Meeting, App } from "@calcom/lib/OgImages";
+import { Meeting, App, Generic } from "@calcom/lib/OgImages";
const calFont = fetch(new URL("../../../../public/fonts/cal.ttf", import.meta.url)).then((res) =>
res.arrayBuffer()
@@ -37,6 +37,12 @@ const appSchema = z.object({
slug: z.string(),
});
+const genericSchema = z.object({
+ imageType: z.literal("generic"),
+ title: z.string(),
+ description: z.string(),
+});
+
export default async function handler(req: NextApiRequest) {
const { searchParams } = new URL(`${req.url}`);
const imageType = searchParams.get("type");
@@ -85,6 +91,17 @@ export default async function handler(req: NextApiRequest) {
});
return new ImageResponse(, ogConfig);
}
+
+ case "generic": {
+ const { title, description } = genericSchema.parse({
+ title: searchParams.get("title"),
+ description: searchParams.get("description"),
+ imageType,
+ });
+
+ return new ImageResponse(, ogConfig);
+ }
+
default:
return new Response("What you're looking for is not here..", { status: 404 });
}
diff --git a/packages/lib/OgImages.tsx b/packages/lib/OgImages.tsx
index d2e0ae7cdc..92d433c2ac 100644
--- a/packages/lib/OgImages.tsx
+++ b/packages/lib/OgImages.tsx
@@ -21,6 +21,11 @@ export interface AppImageProps {
slug: string;
}
+export interface GenericImageProps {
+ title: string;
+ description: string;
+}
+
const joinMultipleNames = (names: string[] = []) => {
const lastName = names.pop();
return `${names.length > 0 ? `${names.join(", ")} & ${lastName}` : lastName}`;
@@ -42,7 +47,7 @@ export const constructMeetingImage = ({ title, users = [], profile }: MeetingIma
profile.image && `&meetingImage=${encodeURIComponent(profile.image)}`,
`${users.map((user) => `&names=${encodeURIComponent(user.name)}`).join("")}`,
`${users.map((user) => `&usernames=${encodeURIComponent(user.username)}`).join("")}`,
- // Joinining a multiline string for readability.
+ // Joining a multiline string for readability.
].join("");
};
@@ -56,7 +61,16 @@ export const constructAppImage = ({ name, slug, description }: AppImageProps): s
`&name=${encodeURIComponent(name)}`,
`&slug=${encodeURIComponent(slug)}`,
`&description=${encodeURIComponent(description)}`,
- // Joinining a multiline string for readability.
+ // Joining a multiline string for readability.
+ ].join("");
+};
+
+export const constructGenericImage = ({ title, description }: GenericImageProps) => {
+ return [
+ `?type=generic`,
+ `&title=${encodeURIComponent(title)}`,
+ `&description=${encodeURIComponent(description)}`,
+ // Joining a multiline string for readability.
].join("");
};
@@ -199,3 +213,22 @@ export const App = ({ name, description, slug }: AppImageProps) => (
);
+
+export const Generic = ({ title, description }: GenericImageProps) => (
+
+
+
+
![Logo]({`${CAL_URL}/cal-logo-word-black.svg`})
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+
+
+);
diff --git a/packages/ui/v2/core/head-seo.tsx b/packages/ui/v2/core/head-seo.tsx
index e4e179d0e1..efe1a2e4be 100644
--- a/packages/ui/v2/core/head-seo.tsx
+++ b/packages/ui/v2/core/head-seo.tsx
@@ -1,10 +1,10 @@
import merge from "lodash/merge";
import { NextSeo, NextSeoProps } from "next-seo";
-import { constructAppImage, constructMeetingImage } from "@calcom/lib/OgImages";
+import { constructAppImage, constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages";
import { getBrowserInfo } from "@calcom/lib/browser/browser.utils";
import { seoConfig, getSeoImage, HeadSeoProps } from "@calcom/lib/next-seo.config";
-import { truncate, truncateOnWord } from "@calcom/lib/text";
+import { truncateOnWord } from "@calcom/lib/text";
/**
* Build full seo tags from title, desc, canonical and url
@@ -55,12 +55,11 @@ const buildSeoMeta = (pageProps: {
export const HeadSeo = (props: HeadSeoProps): JSX.Element => {
const defaultUrl = getBrowserInfo()?.url;
- const image = getSeoImage("default");
const { title, description, siteName, canonical = defaultUrl, nextSeoProps = {}, app, meeting } = props;
- const truncatedDescription = truncate(description, 24);
- const longerTruncatedDescriptionOnWords = truncateOnWord(description, 148);
+ const image = getSeoImage("ogImage") + constructGenericImage({ title, description });
+ const truncatedDescription = truncateOnWord(description, 158);
const pageTitle = title + " | Cal.com";
let seoObject = buildSeoMeta({
@@ -84,7 +83,7 @@ export const HeadSeo = (props: HeadSeoProps): JSX.Element => {
if (app) {
const pageImage =
- getSeoImage("ogImage") + constructAppImage({ ...app, description: longerTruncatedDescriptionOnWords });
+ getSeoImage("ogImage") + constructAppImage({ ...app, description: truncatedDescription });
seoObject = buildSeoMeta({
title: pageTitle,
description: truncatedDescription,