feat(seo): implement google analytics, sitemap, robots and json-ld structured data

This commit is contained in:
Poyraz Avsever
2026-03-30 11:28:05 +03:00
parent e08b9ae708
commit a23665ee9e
6 changed files with 162 additions and 3 deletions
+15 -1
View File
@@ -1,5 +1,19 @@
import { AboutContent } from "@/components/about-content";
import { PersonJsonLd } from "@/components/json-ld";
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 />
</>
);
}
+16 -1
View File
@@ -1,7 +1,10 @@
import { notFound } from "next/navigation";
import { BlogDetailContent } from "@/components/blog-detail-content";
import { ArticleJsonLd } from "@/components/json-ld";
import { getBlogDetailBySlug } from "@/data/blog-detail";
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
type BlogDetailPageProps = {
params: Promise<{ slug: string }>;
};
@@ -14,5 +17,17 @@ export default async function BlogDetailPage({ params }: BlogDetailPageProps) {
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
View File
@@ -1,7 +1,8 @@
import type { Metadata } from "next";
import type { Metadata } from "next";
import "./globals.css";
import { AppShell } from "@/components/app-shell";
import { GoogleAnalytics } from "@/components/google-analytics";
export const metadata: Metadata = {
metadataBase: new URL("https://poyrazavsever.com"),
@@ -85,6 +86,7 @@ export default function RootLayout({
return (
<html lang="tr">
<body className="min-h-dvh bg-background text-foreground antialiased">
<GoogleAnalytics />
<AppShell>{children}</AppShell>
</body>
</html>
+16
View File
@@ -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`,
};
}
+30
View File
@@ -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];
}
+82
View File
@@ -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) }}
/>
);
}