Skip to content

Commit

Permalink
feat: first version of article schema markup
Browse files Browse the repository at this point in the history
  • Loading branch information
gmolki committed Jun 6, 2024
1 parent d7bc95e commit 5d57afc
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 50 deletions.
67 changes: 29 additions & 38 deletions src/pages/_pageMetadataHead.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getFieldFromSources } from "@/utils/getFieldFromSources";
import { BuilderContent } from "@builder.io/sdk";
import Head from "next/head";

Expand All @@ -22,6 +23,7 @@ export type PageMetadataProps = {
canonicalUrl?: PageMetadataField;
openGraph?: PageOpenGraphProps | null;
twitter?: PageTwitterProps | null;
schemaMarkup?: object | null;
};

export const PageMetadataHead = ({
Expand All @@ -32,64 +34,53 @@ export const PageMetadataHead = ({
content: BuilderContent | null;
}) => {
const contentPageMetadata = content?.data?.metadata ?? {};

const getMetaDataField = (keys: string[]) => {
const fromPageMetadata = keys.reduce(
(obj: any, key) => obj?.[key],
pageMetadata
);
const fromContentPageMetadata = keys.reduce(
(obj, key) => obj?.[key],
contentPageMetadata
);
return fromPageMetadata || fromContentPageMetadata;
const getMetaField = (keys: string[]) => {
return getFieldFromSources({
sourcesByPriority: [pageMetadata, contentPageMetadata],
keys,
});
};

const schemaMarkup = getMetaField(["schemaMarkup"]);

return (
<Head>
<title>{getMetaDataField(["title"])}</title>
<meta name="description" content={getMetaDataField(["description"])} />
<link rel="canonical" href={getMetaDataField(["canonicalUrl"])} />
<title>{getMetaField(["title"])}</title>
<meta name="description" content={getMetaField(["description"])} />
<link rel="canonical" href={getMetaField(["canonicalUrl"])} />

{/* Open Graph */}
<meta
property="og:title"
content={getMetaDataField(["openGraph", "title"])}
content={getMetaField(["openGraph", "title"])}
/>
<meta
property="og:description"
content={getMetaDataField(["openGraph", "description"])}
content={getMetaField(["openGraph", "description"])}
/>
<meta
property="og:image"
content={getMetaDataField(["openGraph", "image"])}
/>
<meta
property="og:url"
content={getMetaDataField(["openGraph", "url"])}
content={getMetaField(["openGraph", "image"])}
/>
<meta property="og:url" content={getMetaField(["openGraph", "url"])} />

{/* Twitter */}
<meta
name="twitter:title"
content={getMetaDataField(["twitter", "title"])}
/>
<meta name="twitter:title" content={getMetaField(["twitter", "title"])} />
<meta
name="twitter:description"
content={getMetaDataField(["twitter", "description"])}
/>
<meta
name="twitter:card"
content={getMetaDataField(["twitter", "card"])}
/>
<meta
name="twitter:site"
content={getMetaDataField(["twitter", "site"])}
/>
<meta
name="twitter:image"
content={getMetaDataField(["twitter", "image"])}
content={getMetaField(["twitter", "description"])}
/>
<meta name="twitter:card" content={getMetaField(["twitter", "card"])} />
<meta name="twitter:site" content={getMetaField(["twitter", "site"])} />
<meta name="twitter:image" content={getMetaField(["twitter", "image"])} />

{/* Schema.org */}
{schemaMarkup && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaMarkup) }}
/>
)}
</Head>
);
};
Expand Down
34 changes: 22 additions & 12 deletions src/pages/blog/articles/[blogArticle].tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import React from "react";
import { builder } from "@builder.io/react";
import { GetStaticProps, GetStaticPaths } from "next";
import DynamicPage, { DynamicPageProps } from "@/pages/_dynamicPage";
import { fetchBuilderData } from "@/utils/fetchBuilderData";
import { buildArticleSchemaMarkup } from "@/utils/blog/buildArticleSchemaMarkup";

const PAGE_MODEL = "blog-article";

export const getStaticProps: GetStaticProps = (async ({ params }) => {
const page = await builder
.get(PAGE_MODEL, {
const page = await fetchBuilderData("get", [
PAGE_MODEL,
{
userAttributes: {
urlPath: "/" + ((params?.page as string[])?.join("/") || ""),
urlPath: params?.blogArticle
? `/blog/articles/${params.blogArticle}`
: "",
},
})
.toPromise();
},
]);

const pageProps: DynamicPageProps = {
page: page || null,
pageMetadata: {
schemaMarkup: buildArticleSchemaMarkup(page),
},
};

return {
Expand All @@ -24,13 +31,16 @@ export const getStaticProps: GetStaticProps = (async ({ params }) => {
}) satisfies GetStaticProps;

export const getStaticPaths: GetStaticPaths = async () => {
const pages = await builder.getAll(PAGE_MODEL, {
fields: "data.url",
options: { noTargeting: true },
});
const pages = await fetchBuilderData("getAll", [
PAGE_MODEL,
{
fields: "data.url",
options: { noTargeting: true },
},
]);

const paths = pages
.map((page) => {
.map((page: any) => {
const urlPath = page.data?.url?.split("/").filter(Boolean);
if (urlPath && urlPath.length > 0) {
return {
Expand All @@ -40,7 +50,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
return null;
})
.filter(
(path): path is { params: { blogArticle: string } } => path !== null
(path: any): path is { params: { blogArticle: string } } => path !== null
);

return {
Expand Down
48 changes: 48 additions & 0 deletions src/utils/blog/buildArticleSchemaMarkup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getFieldFromSources } from "../getFieldFromSources";

export const buildArticleSchemaMarkup = (page: any) => {
if (!page) return null;

const formatDate = (timestamp?: string) =>
timestamp ? new Date(timestamp).toISOString() : null;

const typeFromModel = (model: string) => {
switch (model) {
case "schema-org-person":
return "Person";
case "schema-org-organization":
return "Organization";
default:
return null;
}
};

const getField = (keys: string[]) =>
getFieldFromSources({ sourcesByPriority: [page], keys });

const dateModified = formatDate(getField(["lastUpdated"]));
const datePublished = formatDate(getField(["firstPublished"]));

return {
"@context": "https://schema.org",
"@type": "Article",
name: getField(["data", "title"]),
headline: getField(["data", "headline"]),
description: getField(["data", "description"]),
url: getField(["data", "metadata", "canonicalUrl"]),
mainEntityOfPage: getField(["data", "metadata", "canonicalUrl"]),
image: getField(["data", "featureImage"]),
thumbnailUrl: getField(["data", "thumbnailImage"]),
dateCreated: datePublished,
datePublished: datePublished,
dateModified: dateModified,
author: {
"@type": typeFromModel(page.data?.metadata?.author?.model),
...getField(["data", "metadata", "author", "value", "data"]),
},
publisher: {
"@type": typeFromModel(page.data?.metadata?.publisher?.model),
...getField(["data", "metadata", "publisher", "value", "data"]),
},
};
};
18 changes: 18 additions & 0 deletions src/utils/getFieldFromSources.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
interface GetMetdataFieldArgs {
sourcesByPriority: any[];
keys: string[];
}

type GetMetdataFieldResponse = any | null;

export const getFieldFromSources = ({
sourcesByPriority,
keys,
}: GetMetdataFieldArgs): GetMetdataFieldResponse => {
return sourcesByPriority?.reduce((result: any, source) => {
if (result != null) return result;

const value = keys.reduce((obj: any, key) => obj?.[key], source);
return value != null ? value : result;
}, null);
};

0 comments on commit 5d57afc

Please sign in to comment.