feat(seo): implement google analytics, sitemap, robots and json-ld structured data
This commit is contained in:
+15
-1
@@ -1,5 +1,19 @@
|
|||||||
import { AboutContent } from "@/components/about-content";
|
import { AboutContent } from "@/components/about-content";
|
||||||
|
import { PersonJsonLd } from "@/components/json-ld";
|
||||||
|
|
||||||
export default function AboutPage() {
|
export default function AboutPage() {
|
||||||
return <AboutContent />;
|
return (
|
||||||
|
<>
|
||||||
|
<PersonJsonLd
|
||||||
|
name="Poyraz Avsever"
|
||||||
|
jobTitle="Fullstack Developer"
|
||||||
|
sameAs={[
|
||||||
|
"https://github.com/poyrazavsever",
|
||||||
|
"https://www.linkedin.com/in/poyrazavsever",
|
||||||
|
"https://x.com/poyrazavsever",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<AboutContent />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { BlogDetailContent } from "@/components/blog-detail-content";
|
import { BlogDetailContent } from "@/components/blog-detail-content";
|
||||||
|
import { ArticleJsonLd } from "@/components/json-ld";
|
||||||
import { getBlogDetailBySlug } from "@/data/blog-detail";
|
import { getBlogDetailBySlug } from "@/data/blog-detail";
|
||||||
|
|
||||||
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||||
|
|
||||||
type BlogDetailPageProps = {
|
type BlogDetailPageProps = {
|
||||||
params: Promise<{ slug: string }>;
|
params: Promise<{ slug: string }>;
|
||||||
};
|
};
|
||||||
@@ -14,5 +17,17 @@ export default async function BlogDetailPage({ params }: BlogDetailPageProps) {
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
return <BlogDetailContent post={post} />;
|
return (
|
||||||
|
<>
|
||||||
|
<ArticleJsonLd
|
||||||
|
title={post.title}
|
||||||
|
description={post.excerpt}
|
||||||
|
url={`${SITE_URL}/blog/${post.slug}`}
|
||||||
|
image={post.coverImage}
|
||||||
|
datePublished={post.date}
|
||||||
|
authorName={post.author}
|
||||||
|
/>
|
||||||
|
<BlogDetailContent post={post} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -1,7 +1,8 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
import { AppShell } from "@/components/app-shell";
|
import { AppShell } from "@/components/app-shell";
|
||||||
|
import { GoogleAnalytics } from "@/components/google-analytics";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
metadataBase: new URL("https://poyrazavsever.com"),
|
metadataBase: new URL("https://poyrazavsever.com"),
|
||||||
@@ -85,6 +86,7 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="tr">
|
<html lang="tr">
|
||||||
<body className="min-h-dvh bg-background text-foreground antialiased">
|
<body className="min-h-dvh bg-background text-foreground antialiased">
|
||||||
|
<GoogleAnalytics />
|
||||||
<AppShell>{children}</AppShell>
|
<AppShell>{children}</AppShell>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { MetadataRoute } from "next";
|
||||||
|
|
||||||
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||||
|
|
||||||
|
export default function robots(): MetadataRoute.Robots {
|
||||||
|
return {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
userAgent: "*",
|
||||||
|
allow: "/",
|
||||||
|
disallow: ["/api/", "/_next/"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sitemap: `${SITE_URL}/sitemap.xml`,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { listBlogDetails } from "@/data/blog-detail";
|
||||||
|
import type { MetadataRoute } from "next";
|
||||||
|
|
||||||
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||||
|
|
||||||
|
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||||
|
const posts = await listBlogDetails();
|
||||||
|
|
||||||
|
const staticRoutes: MetadataRoute.Sitemap = [
|
||||||
|
{ url: SITE_URL, lastModified: new Date(), changeFrequency: "weekly", priority: 1.0 },
|
||||||
|
{ url: `${SITE_URL}/about`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 },
|
||||||
|
{ url: `${SITE_URL}/about/references`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.5 },
|
||||||
|
{ url: `${SITE_URL}/about/volunteer-community`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.5 },
|
||||||
|
{ url: `${SITE_URL}/blog`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.9 },
|
||||||
|
{ url: `${SITE_URL}/projects`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 },
|
||||||
|
{ url: `${SITE_URL}/content`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.7 },
|
||||||
|
{ url: `${SITE_URL}/contact`, lastModified: new Date(), changeFrequency: "yearly", priority: 0.5 },
|
||||||
|
{ url: `${SITE_URL}/links`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.4 },
|
||||||
|
{ url: `${SITE_URL}/snippets`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.7 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const blogRoutes: MetadataRoute.Sitemap = posts.map((post) => ({
|
||||||
|
url: `${SITE_URL}/blog/${post.slug}`,
|
||||||
|
lastModified: post.date ? new Date(post.date) : new Date(),
|
||||||
|
changeFrequency: "monthly",
|
||||||
|
priority: 0.7,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [...staticRoutes, ...blogRoutes];
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||||
|
|
||||||
|
export type PersonJsonLdProps = {
|
||||||
|
name: string;
|
||||||
|
url?: string;
|
||||||
|
image?: string;
|
||||||
|
jobTitle?: string;
|
||||||
|
sameAs?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function PersonJsonLd({
|
||||||
|
name,
|
||||||
|
url = SITE_URL,
|
||||||
|
image = `${SITE_URL}/logo/logo.jpeg`,
|
||||||
|
jobTitle = "Fullstack Developer",
|
||||||
|
sameAs = [],
|
||||||
|
}: PersonJsonLdProps) {
|
||||||
|
const data = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Person",
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
image,
|
||||||
|
jobTitle,
|
||||||
|
sameAs,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ArticleJsonLdProps = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
url: string;
|
||||||
|
image: string;
|
||||||
|
datePublished: string;
|
||||||
|
authorName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ArticleJsonLd({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
url,
|
||||||
|
image,
|
||||||
|
datePublished,
|
||||||
|
authorName = "Poyraz Avsever",
|
||||||
|
}: ArticleJsonLdProps) {
|
||||||
|
const data = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Article",
|
||||||
|
headline: title,
|
||||||
|
description,
|
||||||
|
url,
|
||||||
|
image: image.startsWith("http") ? image : `${SITE_URL}${image}`,
|
||||||
|
datePublished,
|
||||||
|
author: {
|
||||||
|
"@type": "Person",
|
||||||
|
name: authorName,
|
||||||
|
url: SITE_URL,
|
||||||
|
},
|
||||||
|
publisher: {
|
||||||
|
"@type": "Person",
|
||||||
|
name: authorName,
|
||||||
|
logo: {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
url: `${SITE_URL}/logo/logo.jpeg`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user