aboutsummaryrefslogtreecommitdiffstats
path: root/src/pages
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-01-19 13:56:34 +0100
committerArmand Philippot <git@armandphilippot.com>2022-01-19 14:22:28 +0100
commita26b775b7bbf1abd3e99c8bf9ce4c7522d3a0adc (patch)
tree7f041845fa64d00f20f949d1cba14fec3eca3435 /src/pages
parent813084fc23113ae2f594bf6ef1cf53bd003c9479 (diff)
chore: add structured data using schema.org and JSON-LD
I also added the featured image on single article.
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/article/[slug].tsx67
-rw-r--r--src/pages/blog/index.tsx38
-rw-r--r--src/pages/contact.tsx40
-rw-r--r--src/pages/cv.tsx48
-rw-r--r--src/pages/index.tsx30
-rw-r--r--src/pages/mentions-legales.tsx50
-rw-r--r--src/pages/sujet/[slug].tsx52
-rw-r--r--src/pages/thematique/[slug].tsx47
8 files changed, 369 insertions, 3 deletions
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx
index e519c27..8c345b7 100644
--- a/src/pages/article/[slug].tsx
+++ b/src/pages/article/[slug].tsx
@@ -18,6 +18,7 @@ import { useEffect } from 'react';
import styles from '@styles/pages/Page.module.scss';
import { Sharing, ToC } from '@components/Widgets';
import Sidebar from '@components/Sidebar/Sidebar';
+import { Blog, BlogPosting, Graph, WebPage } from 'schema-dts';
const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => {
const {
@@ -26,6 +27,7 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => {
content,
databaseId,
dates,
+ featuredImage,
intro,
seo,
subjects,
@@ -52,13 +54,74 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => {
translateCopyButton(locale);
}, [locale]);
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}${router.asPath}`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ lastReviewed: dates.update,
+ name: seo.title,
+ description: seo.metaDesc,
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}${router.asPath}`,
+ isPartOf: {
+ '@id': `${config.url}`,
+ },
+ };
+
+ const blogSchema: Blog = {
+ '@id': `${config.url}/#blog`,
+ '@type': 'Blog',
+ blogPost: { '@id': `${config.url}/#article` },
+ isPartOf: {
+ '@id': `${config.url}${router.asPath}`,
+ },
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ };
+
+ const publicationDate = new Date(dates.publication);
+ const updateDate = new Date(dates.update);
+
+ const blogPostSchema: BlogPosting = {
+ '@id': `${config.url}/#article`,
+ '@type': 'BlogPosting',
+ name: title,
+ description: intro,
+ articleBody: content,
+ author: { '@id': `${config.url}/#branding` },
+ commentCount: comments.length,
+ copyrightYear: publicationDate.getFullYear(),
+ creator: { '@id': `${config.url}/#branding` },
+ dateCreated: publicationDate.toISOString(),
+ dateModified: updateDate.toISOString(),
+ datePublished: publicationDate.toISOString(),
+ discussionUrl: `${config.url}${router.asPath}/#comments`,
+ editor: { '@id': `${config.url}/#branding` },
+ image: featuredImage?.sourceUrl,
+ inLanguage: config.defaultLocale,
+ isPartOf: {
+ '@id': `${config.url}/blog`,
+ },
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ mainEntityOfPage: { '@id': `${config.url}${router.asPath}` },
+ thumbnailUrl: featuredImage?.sourceUrl,
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema, blogSchema, blogPostSchema],
+ };
+
return (
<>
<Head>
<title>{seo.title}</title>
<meta name="description" content={seo.metaDesc} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
- <article className={styles.article}>
+ <article id="article" className={styles.article}>
<PostHeader intro={intro} meta={meta} title={title} />
<Sidebar position="left">
<ToC />
@@ -73,7 +136,7 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => {
</Sidebar>
<section id="comments" className={styles.comments}>
<CommentsList articleId={databaseId} comments={comments} />
- <CommentForm articleId={post.databaseId} />
+ <CommentForm articleId={databaseId} />
</section>
</article>
</>
diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx
index 48fab1c..765a93b 100644
--- a/src/pages/blog/index.tsx
+++ b/src/pages/blog/index.tsx
@@ -17,9 +17,12 @@ import Sidebar from '@components/Sidebar/Sidebar';
import styles from '@styles/pages/Page.module.scss';
import { useRef } from 'react';
import Spinner from '@components/Spinner/Spinner';
+import { Blog as BlogSchema, Graph, WebPage } from 'schema-dts';
+import { useRouter } from 'next/router';
const Blog: NextPageWithLayout<BlogPageProps> = ({ fallback }) => {
const lastPostRef = useRef<HTMLSpanElement>(null);
+ const router = useRouter();
const getKey = (pageIndex: number, previousData: PostsListData) => {
if (previousData && !previousData.posts) return null;
@@ -59,13 +62,48 @@ const Blog: NextPageWithLayout<BlogPageProps> = ({ fallback }) => {
return <PostsList ref={lastPostRef} data={data} showYears={true} />;
};
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}${router.asPath}`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ name: seo.blog.title,
+ description: seo.blog.description,
+ inLanguage: config.defaultLocale,
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}`,
+ isPartOf: {
+ '@id': `${config.url}`,
+ },
+ };
+
+ const blogSchema: BlogSchema = {
+ '@id': `${config.url}/#blog`,
+ '@type': 'Blog',
+ author: { '@id': `${config.url}/#branding` },
+ creator: { '@id': `${config.url}/#branding` },
+ editor: { '@id': `${config.url}/#branding` },
+ inLanguage: config.defaultLocale,
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ mainEntityOfPage: { '@id': `${config.url}${router.asPath}` },
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema, blogSchema],
+ };
+
return (
<>
<Head>
<title>{seo.blog.title}</title>
<meta name="description" content={seo.blog.description} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
<article
+ id="blog"
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader title={t`Blog`} />
diff --git a/src/pages/contact.tsx b/src/pages/contact.tsx
index bafa5e9..ba462c0 100644
--- a/src/pages/contact.tsx
+++ b/src/pages/contact.tsx
@@ -13,6 +13,9 @@ import PostHeader from '@components/PostHeader/PostHeader';
import styles from '@styles/pages/Page.module.scss';
import { SocialMedia } from '@components/Widgets';
import Sidebar from '@components/Sidebar/Sidebar';
+import { ContactPage as ContactPageSchema, Graph, WebPage } from 'schema-dts';
+import { config } from '@config/website';
+import { useRouter } from 'next/router';
const ContactPage: NextPageWithLayout = () => {
const [name, setName] = useState('');
@@ -20,6 +23,7 @@ const ContactPage: NextPageWithLayout = () => {
const [subject, setSubject] = useState('');
const [message, setMessage] = useState('');
const [status, setStatus] = useState('');
+ const router = useRouter();
const resetForm = () => {
setName('');
@@ -55,13 +59,49 @@ const ContactPage: NextPageWithLayout = () => {
const title = t`Contact`;
const intro = t`Please fill the form to contact me.`;
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}${router.asPath}`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ name: seo.contact.title,
+ description: seo.contact.description,
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}${router.asPath}`,
+ isPartOf: {
+ '@id': `${config.url}`,
+ },
+ };
+
+ const contactSchema: ContactPageSchema = {
+ '@id': `${config.url}/#contact`,
+ '@type': 'ContactPage',
+ name: title,
+ description: intro,
+ author: { '@id': `${config.url}/#branding` },
+ creator: { '@id': `${config.url}/#branding` },
+ editor: { '@id': `${config.url}/#branding` },
+ inLanguage: config.defaultLocale,
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ mainEntityOfPage: { '@id': `${config.url}${router.asPath}` },
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema, contactSchema],
+ };
+
return (
<>
<Head>
<title>{seo.contact.title}</title>
<meta name="description" content={seo.contact.description} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
<article
+ id="contact"
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader title={title} intro={intro} />
diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx
index 01eab4c..78e9a6e 100644
--- a/src/pages/cv.tsx
+++ b/src/pages/cv.tsx
@@ -11,8 +11,12 @@ import styles from '@styles/pages/Page.module.scss';
import { CVPreview, SocialMedia, ToC } from '@components/Widgets';
import { t } from '@lingui/macro';
import Sidebar from '@components/Sidebar/Sidebar';
+import { AboutPage, Graph, WebPage } from 'schema-dts';
+import { config } from '@config/website';
+import { useRouter } from 'next/router';
const CV: NextPageWithLayout = () => {
+ const router = useRouter();
const dates = {
publication: meta.publishedOn,
update: meta.updatedOn,
@@ -22,13 +26,57 @@ const CV: NextPageWithLayout = () => {
dates,
};
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}${router.asPath}`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ name: seo.cv.title,
+ description: seo.cv.description,
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}${router.asPath}`,
+ isPartOf: {
+ '@id': `${config.url}`,
+ },
+ };
+
+ const publicationDate = new Date(dates.publication);
+ const updateDate = new Date(dates.update);
+
+ const cvSchema: AboutPage = {
+ '@id': `${config.url}/#cv`,
+ '@type': 'AboutPage',
+ name: `${config.name} CV`,
+ description: intro,
+ author: { '@id': `${config.url}/#branding` },
+ creator: { '@id': `${config.url}/#branding` },
+ dateCreated: publicationDate.toISOString(),
+ dateModified: updateDate.toISOString(),
+ datePublished: publicationDate.toISOString(),
+ editor: { '@id': `${config.url}/#branding` },
+ image,
+ inLanguage: config.defaultLocale,
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ thumbnailUrl: image,
+ mainEntityOfPage: { '@id': `${config.url}${router.asPath}` },
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema, cvSchema],
+ };
+
return (
<>
<Head>
<title>{seo.cv.title}</title>
<meta name="description" content={seo.cv.description} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
<article
+ id="cv"
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader intro={intro} meta={pageMeta} title={meta.title} />
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 3664ae1..f59602f 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -11,6 +11,8 @@ import styles from '@styles/pages/Home.module.scss';
import { t } from '@lingui/macro';
import FeedIcon from '@assets/images/icon-feed.svg';
import { ContactIcon } from '@components/Icons';
+import { Graph, WebPage } from 'schema-dts';
+import { config } from '@config/website';
const Home: NextPageWithLayout = () => {
const CodingLinks = () => {
@@ -90,13 +92,39 @@ const Home: NextPageWithLayout = () => {
MoreLinks: MoreLinks,
};
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}/#home`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ name: seo.legalNotice.title,
+ description: seo.legalNotice.description,
+ author: { '@id': `${config.url}/#branding` },
+ creator: { '@id': `${config.url}/#branding` },
+ editor: { '@id': `${config.url}/#branding` },
+ inLanguage: config.defaultLocale,
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}`,
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema],
+ };
+
return (
<>
<Head>
<title>{seo.homepage.title}</title>
<meta name="description" content={seo.homepage.description} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
- <HomePageContent components={components} />
+ <div id="home">
+ <HomePageContent components={components} />
+ </div>
</>
);
};
diff --git a/src/pages/mentions-legales.tsx b/src/pages/mentions-legales.tsx
index fcaef06..81d8e98 100644
--- a/src/pages/mentions-legales.tsx
+++ b/src/pages/mentions-legales.tsx
@@ -13,8 +13,13 @@ import { ArticleMeta } from '@ts/types/articles';
import styles from '@styles/pages/Page.module.scss';
import { ToC } from '@components/Widgets';
import Sidebar from '@components/Sidebar/Sidebar';
+import { Article, Graph, WebPage } from 'schema-dts';
+import { config } from '@config/website';
+import { useRouter } from 'next/router';
+import { t } from '@lingui/macro';
const LegalNotice: NextPageWithLayout = () => {
+ const router = useRouter();
const dates = {
publication: meta.publishedOn,
update: meta.updatedOn,
@@ -24,13 +29,58 @@ const LegalNotice: NextPageWithLayout = () => {
dates,
};
+ const publicationDate = new Date(dates.publication);
+ const updateDate = new Date(dates.update);
+
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}${router.asPath}`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ name: seo.legalNotice.title,
+ description: seo.legalNotice.description,
+ inLanguage: config.defaultLocale,
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}${router.asPath}`,
+ isPartOf: {
+ '@id': `${config.url}`,
+ },
+ };
+
+ const articleSchema: Article = {
+ '@id': `${config.url}/#legal-notice`,
+ '@type': 'Article',
+ name: t`Legal notice`,
+ description: intro,
+ author: { '@id': `${config.url}/#branding` },
+ copyrightYear: publicationDate.getFullYear(),
+ creator: { '@id': `${config.url}/#branding` },
+ dateCreated: publicationDate.toISOString(),
+ dateModified: updateDate.toISOString(),
+ datePublished: publicationDate.toISOString(),
+ editor: { '@id': `${config.url}/#branding` },
+ inLanguage: config.defaultLocale,
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ mainEntityOfPage: { '@id': `${config.url}${router.asPath}` },
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema, articleSchema],
+ };
+
return (
<>
<Head>
<title>{seo.legalNotice.title}</title>
<meta name="description" content={seo.legalNotice.description} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
<article
+ id="legal-notice"
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader intro={intro} meta={pageMeta} title={meta.title} />
diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx
index b373041..97c76c0 100644
--- a/src/pages/sujet/[slug].tsx
+++ b/src/pages/sujet/[slug].tsx
@@ -17,9 +17,13 @@ import { RelatedThematics, ToC, TopicsList } from '@components/Widgets';
import { useRef } from 'react';
import Head from 'next/head';
import Sidebar from '@components/Sidebar/Sidebar';
+import { Article as Article, Blog, Graph, WebPage } from 'schema-dts';
+import { config } from '@config/website';
+import { useRouter } from 'next/router';
const Subject: NextPageWithLayout<SubjectProps> = ({ subject }) => {
const relatedThematics = useRef<ThematicPreview[]>([]);
+ const router = useRouter();
const updateRelatedThematics = (newThematics: ThematicPreview[]) => {
newThematics.forEach((thematic) => {
@@ -49,13 +53,61 @@ const Subject: NextPageWithLayout<SubjectProps> = ({ subject }) => {
website: subject.officialWebsite,
};
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}${router.asPath}`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ name: subject.seo.title,
+ description: subject.seo.metaDesc,
+ inLanguage: config.defaultLocale,
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}`,
+ isPartOf: {
+ '@id': `${config.url}`,
+ },
+ };
+
+ const publicationDate = new Date(subject.dates.publication);
+ const updateDate = new Date(subject.dates.update);
+
+ const articleSchema: Article = {
+ '@id': `${config.url}/subject`,
+ '@type': 'Article',
+ name: subject.title,
+ description: subject.intro,
+ author: { '@id': `${config.url}/#branding` },
+ copyrightYear: publicationDate.getFullYear(),
+ creator: { '@id': `${config.url}/#branding` },
+ dateCreated: publicationDate.toISOString(),
+ dateModified: updateDate.toISOString(),
+ datePublished: publicationDate.toISOString(),
+ editor: { '@id': `${config.url}/#branding` },
+ thumbnailUrl: subject.featuredImage?.sourceUrl,
+ image: subject.featuredImage?.sourceUrl,
+ inLanguage: config.defaultLocale,
+ isPartOf: { '@id': `${config.url}/blog` },
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ mainEntityOfPage: { '@id': `${config.url}${router.asPath}` },
+ subjectOf: { '@id': `${config.url}/blog` },
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema, articleSchema],
+ };
+
return (
<>
<Head>
<title>{subject.seo.title}</title>
<meta name="description" content={subject.seo.metaDesc} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
<article
+ id="subject"
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader
diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx
index 4eee656..660a207 100644
--- a/src/pages/thematique/[slug].tsx
+++ b/src/pages/thematique/[slug].tsx
@@ -17,9 +17,13 @@ import { useRef } from 'react';
import { ArticleMeta } from '@ts/types/articles';
import Head from 'next/head';
import Sidebar from '@components/Sidebar/Sidebar';
+import { Article, Blog, Graph, WebPage } from 'schema-dts';
+import { config } from '@config/website';
+import { useRouter } from 'next/router';
const Thematic: NextPageWithLayout<ThematicProps> = ({ thematic }) => {
const relatedSubjects = useRef<SubjectPreview[]>([]);
+ const router = useRouter();
const updateRelatedSubjects = (newSubjects: SubjectPreview[]) => {
newSubjects.forEach((subject) => {
@@ -48,13 +52,56 @@ const Thematic: NextPageWithLayout<ThematicProps> = ({ thematic }) => {
dates: thematic.dates,
};
+ const webpageSchema: WebPage = {
+ '@id': `${config.url}${router.asPath}`,
+ '@type': 'WebPage',
+ breadcrumb: { '@id': `${config.url}/#breadcrumb` },
+ name: thematic.seo.title,
+ description: thematic.seo.metaDesc,
+ inLanguage: config.defaultLocale,
+ reviewedBy: { '@id': `${config.url}/#branding` },
+ url: `${config.url}`,
+ };
+
+ const publicationDate = new Date(thematic.dates.publication);
+ const updateDate = new Date(thematic.dates.update);
+
+ const articleSchema: Article = {
+ '@id': `${config.url}/thematic`,
+ '@type': 'Article',
+ name: thematic.title,
+ description: thematic.intro,
+ author: { '@id': `${config.url}/#branding` },
+ copyrightYear: publicationDate.getFullYear(),
+ creator: { '@id': `${config.url}/#branding` },
+ dateCreated: publicationDate.toISOString(),
+ dateModified: updateDate.toISOString(),
+ datePublished: publicationDate.toISOString(),
+ editor: { '@id': `${config.url}/#branding` },
+ inLanguage: config.defaultLocale,
+ isPartOf: { '@id': `${config.url}/blog` },
+ license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
+ mainEntityOfPage: { '@id': `${config.url}${router.asPath}` },
+ subjectOf: { '@id': `${config.url}/blog` },
+ };
+
+ const schemaJsonLd: Graph = {
+ '@context': 'https://schema.org',
+ '@graph': [webpageSchema, articleSchema],
+ };
+
return (
<>
<Head>
<title>{thematic.seo.title}</title>
<meta name="description" content={thematic.seo.metaDesc} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></script>
</Head>
<article
+ id="thematic"
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader intro={thematic.intro} meta={meta} title={thematic.title} />