Add support for rich snippets
This commit is contained in:
@ -1,73 +1,163 @@
|
|||||||
import NextHead from 'next/head';
|
import NextHead from 'next/head';
|
||||||
import siteConfig from '../content/site.json';
|
import siteConfig from '../content/site.json';
|
||||||
|
import { RoadmapType } from '../lib/roadmap';
|
||||||
|
import { roadmapTheme } from '../styles/theme';
|
||||||
|
|
||||||
type HelmetProps = {
|
type HelmetProps = {
|
||||||
title?: string;
|
title?: string;
|
||||||
keywords?: string[];
|
keywords?: string[];
|
||||||
canonical?: string;
|
canonical?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
roadmap?: RoadmapType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Helmet = (props: HelmetProps) => (
|
function getRichSnippetJson(roadmap: RoadmapType) {
|
||||||
<NextHead>
|
return {
|
||||||
<meta charSet='UTF-8' />
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'Article',
|
||||||
|
mainEntityOfPage: {
|
||||||
|
'@type': 'WebPage',
|
||||||
|
'@id': `https://roadmap.sh/${roadmap.id}`,
|
||||||
|
},
|
||||||
|
headline: roadmap.seo.title,
|
||||||
|
description: roadmap.seo.description,
|
||||||
|
image: roadmap.jsonUrl
|
||||||
|
? `https://roadmap.sh/roadmaps/${roadmap.id}.png`
|
||||||
|
: undefined,
|
||||||
|
author: {
|
||||||
|
'@type': 'Person',
|
||||||
|
name: 'Kamran Ahmed',
|
||||||
|
url: 'https://twitter.com/kamranahmedse',
|
||||||
|
},
|
||||||
|
publisher: {
|
||||||
|
'@type': 'Organization',
|
||||||
|
name: 'roadmap.sh',
|
||||||
|
logo: {
|
||||||
|
'@type': 'ImageObject',
|
||||||
|
url: 'https://roadmap.sh/brand-square.png',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
<title>{props.title || siteConfig.title}</title>
|
const Helmet = (props: HelmetProps) => {
|
||||||
<meta name='description' content={props.description || siteConfig.description} />
|
const { roadmap, title, canonical, description, keywords } = props;
|
||||||
|
|
||||||
<meta name='author' content={siteConfig.author} />
|
return (
|
||||||
<meta name='keywords' content={props.keywords ? props.keywords.join(',') : siteConfig.keywords.join(',')} />
|
<NextHead>
|
||||||
|
<meta charSet="UTF-8" />
|
||||||
|
|
||||||
<meta name='viewport'
|
<title>{title || siteConfig.title}</title>
|
||||||
content='width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0, minimum-scale=1.0' />
|
<meta
|
||||||
{props.canonical && <link rel='canonical' href={props.canonical} />}
|
name="description"
|
||||||
<meta httpEquiv='Content-Language' content='en' />
|
content={description || siteConfig.description}
|
||||||
|
/>
|
||||||
|
|
||||||
<meta property='og:title' content={props.title || siteConfig.title} />
|
<meta name="author" content={siteConfig.author} />
|
||||||
<meta property='og:description' content={props.description || siteConfig.description} />
|
<meta
|
||||||
<meta property='og:image' content={`${siteConfig.url.web}${siteConfig.logoSquare}`} />
|
name="keywords"
|
||||||
<meta property='og:url' content={siteConfig.url.web} />
|
content={keywords ? keywords.join(',') : siteConfig.keywords.join(',')}
|
||||||
<meta property='og:type' content='website' />
|
/>
|
||||||
<meta property='article:publisher' content={`https://facebook.com/${siteConfig.facebook}`} />
|
|
||||||
<meta property='og:site_name' content={siteConfig.name} />
|
|
||||||
<meta property='article:author' content={siteConfig.author} />
|
|
||||||
|
|
||||||
<meta name='twitter:card' content='summary' />
|
<meta
|
||||||
<meta name='twitter:site' content={`@${siteConfig.twitter}`} />
|
name="viewport"
|
||||||
<meta name='twitter:title' content={props.title || siteConfig.title} />
|
content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0, minimum-scale=1.0"
|
||||||
<meta name='twitter:description' content={props.description || siteConfig.description} />
|
/>
|
||||||
<meta name='twitter:image' content={`${siteConfig.url.web}${siteConfig.logoSquare}`} />
|
{canonical && <link rel="canonical" href={canonical} />}
|
||||||
<meta name='twitter:image:alt' content='roadmap.sh' />
|
<meta httpEquiv="Content-Language" content="en" />
|
||||||
|
|
||||||
<meta name='mobile-web-app-capable' content='yes' />
|
<meta property="og:title" content={title || siteConfig.title} />
|
||||||
<meta name='apple-mobile-web-app-capable' content='yes' />
|
<meta
|
||||||
<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />
|
property="og:description"
|
||||||
<link rel='apple-touch-icon' sizes='180x180' href='/manifest/apple-touch-icon.png' />
|
content={description || siteConfig.description}
|
||||||
<meta name='msapplication-TileColor' content='#101010' />
|
/>
|
||||||
<meta name='theme-color' content='#848a9a' />
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content={`${siteConfig.url.web}${siteConfig.logoSquare}`}
|
||||||
|
/>
|
||||||
|
<meta property="og:url" content={siteConfig.url.web} />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta
|
||||||
|
property="article:publisher"
|
||||||
|
content={`https://facebook.com/${siteConfig.facebook}`}
|
||||||
|
/>
|
||||||
|
<meta property="og:site_name" content={siteConfig.name} />
|
||||||
|
<meta property="article:author" content={siteConfig.author} />
|
||||||
|
|
||||||
<link rel='manifest' href='/manifest/manifest.json' />
|
<meta name="twitter:card" content="summary" />
|
||||||
<link rel='icon' type='image/png' sizes='32x32' href='/manifest/icon32.png' />
|
<meta name="twitter:site" content={`@${siteConfig.twitter}`} />
|
||||||
<link rel='icon' type='image/png' sizes='16x16' href='/manifest/icon16.png' />
|
<meta name="twitter:title" content={title || siteConfig.title} />
|
||||||
<link rel='shortcut icon' href='/manifest/favicon.ico' type='image/x-icon' />
|
<meta
|
||||||
<link rel='icon' href='/manifest/favicon.ico' type='image/x-icon' />
|
name="twitter:description"
|
||||||
|
content={description || siteConfig.description}
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content={`${siteConfig.url.web}${siteConfig.logoSquare}`}
|
||||||
|
/>
|
||||||
|
<meta name="twitter:image:alt" content="roadmap.sh" />
|
||||||
|
|
||||||
{ /* Global Site Tag (gtag.js) - Google Analytics */}
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
{process.env.GA_SECRET && (
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<>
|
<meta
|
||||||
<script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GA_SECRET}`} />
|
name="apple-mobile-web-app-status-bar-style"
|
||||||
<script dangerouslySetInnerHTML={{
|
content="black-translucent"
|
||||||
__html: `
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="/manifest/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<meta name="msapplication-TileColor" content="#101010" />
|
||||||
|
<meta name="theme-color" content="#848a9a" />
|
||||||
|
|
||||||
|
<link rel="manifest" href="/manifest/manifest.json" />
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/manifest/icon32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/manifest/icon16.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="shortcut icon"
|
||||||
|
href="/manifest/favicon.ico"
|
||||||
|
type="image/x-icon"
|
||||||
|
/>
|
||||||
|
<link rel="icon" href="/manifest/favicon.ico" type="image/x-icon" />
|
||||||
|
|
||||||
|
{roadmap?.id && (
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{JSON.stringify(getRichSnippetJson(roadmap))}
|
||||||
|
</script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Global Site Tag (gtag.js) - Google Analytics */}
|
||||||
|
{process.env.GA_SECRET && (
|
||||||
|
<>
|
||||||
|
<script
|
||||||
|
async
|
||||||
|
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GA_SECRET}`}
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag(){dataLayer.push(arguments);}
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
gtag('config', '${process.env.GA_SECRET}');
|
gtag('config', '${process.env.GA_SECRET}');
|
||||||
`
|
`,
|
||||||
}} />
|
}}
|
||||||
</>
|
/>
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
</NextHead>
|
</NextHead>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Helmet;
|
export default Helmet;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"seo": {
|
"seo": {
|
||||||
"title": "Learn to become a modern frontend developer",
|
"title": "Frontend Developer Roadmap: Learn to become a modern frontend developer",
|
||||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for modern frontend development. Learn to become a modern frontend developer by following the steps, skills, resources and guides listed in this roadmap.",
|
"description": "Learn to become a modern frontend developer using this roadmap. Community driven, articles, resources, guides, interview questions, quizzes for modern frontend development.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"guide to becoming a developer",
|
"guide to becoming a developer",
|
||||||
"guide to becoming a frontend developer",
|
"guide to becoming a frontend developer",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"seo": {
|
"seo": {
|
||||||
"title": "Learn to become a modern backend developer",
|
"title": "Backend Developer Roadmap: Learn to become a modern backend developer",
|
||||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for modern backend development. Learn to become a modern backend developer by following the steps, skills, resources and guides listed in this roadmap.",
|
"description": "Learn to become a modern backend developer using this roadmap. Community driven, articles, resources, guides, interview questions, quizzes for modern backend development.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"guide to becoming a developer",
|
"guide to becoming a developer",
|
||||||
"guide to becoming a backend developer",
|
"guide to becoming a backend developer",
|
||||||
|
@ -63,6 +63,7 @@ export default function Roadmap(props: RoadmapProps) {
|
|||||||
title={roadmap?.seo?.title || roadmap.title}
|
title={roadmap?.seo?.title || roadmap.title}
|
||||||
description={roadmap?.seo?.description || roadmap.description}
|
description={roadmap?.seo?.description || roadmap.description}
|
||||||
keywords={roadmap?.seo.keywords || []}
|
keywords={roadmap?.seo.keywords || []}
|
||||||
|
roadmap={roadmap}
|
||||||
/>
|
/>
|
||||||
<Box mb="60px">
|
<Box mb="60px">
|
||||||
<RoadmapPageHeader roadmap={roadmap} />
|
<RoadmapPageHeader roadmap={roadmap} />
|
||||||
|
BIN
public/roadmaps/android.png
Normal file
BIN
public/roadmaps/android.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 93 KiB |
Reference in New Issue
Block a user