feat: AI SEO optimization - JSON-LD schemas, per-page metadata, dynamic lesson metadata

This commit is contained in:
AyrisAI
2026-07-25 11:11:44 +03:00
parent d88cf955c7
commit 84acfadf96
5 changed files with 299 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
import type { Metadata } from 'next';
const BASE_URL = 'https://ayrisai.xyz';
export const metadata: Metadata = {
title: 'Developer Cheatsheets & Quick Reference | ayris.tech',
description:
'Free developer cheatsheets for Git, Next.js App Router, Docker, TypeScript, Python, SQL, and more. Quick-copy command references used in ayris.tech YouTube tutorials.',
keywords: [
'developer cheatsheet', 'Git commands cheatsheet', 'Next.js cheatsheet',
'Docker commands', 'TypeScript quick reference', 'SQL cheatsheet',
'programming reference card', 'ayris.tech',
],
alternates: {
canonical: `${BASE_URL}/en/cheatsheets`,
},
openGraph: {
title: 'Developer Cheatsheets & Quick Reference | ayris.tech',
description: 'Free copy-paste developer cheatsheets for Git, Next.js, Docker, TypeScript, and more.',
url: `${BASE_URL}/en/cheatsheets`,
type: 'website',
},
};
export default function CheatsheetsLayout({ children }: { children: React.ReactNode }) {
return (
<>
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'CollectionPage',
name: 'Developer Cheatsheets & Quick Reference — ayris.tech',
description:
'Free developer cheatsheets for Git, Next.js, Docker, TypeScript, Python, SQL, and more.',
url: `${BASE_URL}/en/cheatsheets`,
publisher: {
'@type': 'Person',
name: 'ayris.tech',
url: BASE_URL,
},
}),
}}
/>
{children}
</>
);
}
+37
View File
@@ -89,6 +89,43 @@ export default async function RootLayout({
className={`${jakartaSans.variable} ${jetbrainsMono.variable} dark h-full antialiased`}
>
<body className="min-h-full flex flex-col bg-slate-950 text-slate-100 font-sans selection:bg-red-500 selection:text-white tracking-normal leading-relaxed" suppressHydrationWarning>
{/* Organization + WebSite JSON-LD — global entity recognition */}
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: JSON.stringify([
{
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'ayris.tech',
url: BASE_URL,
logo: `${BASE_URL}/logo.jpeg`,
sameAs: [
'https://www.youtube.com/@ayristech',
'https://github.com/ayrisdev',
'https://git.ayris.tech/ayrisdev',
],
description:
'ayris.tech is a YouTube channel and resource portal publishing tutorials on Next.js, React, TypeScript, Python, AI agents, Docker, and modern full-stack development.',
},
{
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'ayris.tech YouTube Portal',
url: BASE_URL,
description:
'Source code, lesson notes, cheatsheets, AI prompts, and project downloads for every ayris.tech YouTube tutorial.',
publisher: { '@type': 'Organization', name: 'ayris.tech', url: BASE_URL },
potentialAction: {
'@type': 'SearchAction',
target: { '@type': 'EntryPoint', urlTemplate: `${BASE_URL}/en/lessons?q={search_term_string}` },
'query-input': 'required name=search_term_string',
},
},
]),
}}
/>
<NextIntlClientProvider messages={messages}>
<Navbar locale={locale} />
<main className="flex-1 flex flex-col">
+110
View File
@@ -0,0 +1,110 @@
import type { Metadata } from 'next';
import { getLessonBySlug } from '@/lib/actions/lessonActions';
const BASE_URL = 'https://ayrisai.xyz';
interface Props {
params: Promise<{ locale: string; slug: string }>;
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug, locale } = await params;
const lesson = await getLessonBySlug(slug);
if (!lesson) {
return {
title: 'Lesson Not Found | ayris.tech',
};
}
const url = `${BASE_URL}/${locale}/lessons/${slug}`;
const description =
lesson.summary ||
`Watch the ${lesson.title} tutorial on ayris.tech YouTube. Download source code, read lesson notes, and grab the full project starter pack.`;
return {
title: lesson.title,
description,
keywords: [
...(lesson.tags || []),
'YouTube tutorial', 'source code download', 'ayris.tech', lesson.category,
],
alternates: { canonical: url },
openGraph: {
title: lesson.title,
description,
url,
type: 'video.other',
images: lesson.thumbnailUrl
? [{ url: lesson.thumbnailUrl, width: 1280, height: 720, alt: lesson.title }]
: [],
},
twitter: {
card: 'summary_large_image',
title: lesson.title,
description,
images: lesson.thumbnailUrl ? [lesson.thumbnailUrl] : [],
},
};
}
export default async function LessonDetailLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string; slug: string }>;
}) {
const { slug, locale } = await params;
const lesson = await getLessonBySlug(slug);
const jsonLd = lesson
? {
'@context': 'https://schema.org',
'@graph': [
{
'@type': 'VideoObject',
name: lesson.title,
description: lesson.summary || lesson.title,
thumbnailUrl: lesson.thumbnailUrl || '',
uploadDate: lesson.createdAt || new Date().toISOString(),
duration: lesson.duration
? `PT${lesson.duration.replace(':', 'M')}S`
: undefined,
contentUrl: lesson.youtubeUrl || '',
embedUrl: lesson.youtubeId
? `https://www.youtube.com/embed/${lesson.youtubeId}`
: undefined,
author: { '@type': 'Person', name: 'ayris.tech', url: BASE_URL },
publisher: {
'@type': 'Organization',
name: 'ayris.tech',
url: BASE_URL,
logo: { '@type': 'ImageObject', url: `${BASE_URL}/logo.jpeg` },
},
},
{
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: `${BASE_URL}/${locale}` },
{ '@type': 'ListItem', position: 2, name: 'Lessons', item: `${BASE_URL}/${locale}/lessons` },
{ '@type': 'ListItem', position: 3, name: lesson.title, item: `${BASE_URL}/${locale}/lessons/${slug}` },
],
},
],
}
: null;
return (
<>
{jsonLd && (
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
)}
{children}
</>
);
}
+52
View File
@@ -0,0 +1,52 @@
import type { Metadata } from 'next';
const BASE_URL = 'https://ayrisai.xyz';
export const metadata: Metadata = {
title: 'YouTube Lessons & Source Code | ayris.tech',
description:
'Browse all ayris.tech YouTube tutorial source code, lesson notes, starter packages, and project downloads. Covering Next.js, React, Python, TypeScript, Tailwind CSS, AI, and more.',
keywords: [
'Next.js tutorial source code', 'React tutorial download',
'YouTube code resources', 'TypeScript tutorial',
'Tailwind CSS examples', 'full-stack web development',
'ayris.tech YouTube', 'Python AI tutorial',
],
alternates: {
canonical: `${BASE_URL}/en/lessons`,
},
openGraph: {
title: 'YouTube Lessons & Source Code | ayris.tech',
description: 'Download source code and browse lesson notes for every ayris.tech YouTube tutorial.',
url: `${BASE_URL}/en/lessons`,
type: 'website',
},
};
export default function LessonsLayout({ children }: { children: React.ReactNode }) {
return (
<>
{/* WebSite + ItemList Schema for lesson listing */}
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'CollectionPage',
name: 'YouTube Lessons & Source Code — ayris.tech',
description:
'Source code, lesson notes, and project downloads for every ayris.tech YouTube tutorial.',
url: `${BASE_URL}/en/lessons`,
publisher: {
'@type': 'Person',
name: 'ayris.tech',
url: BASE_URL,
},
}),
}}
/>
{children}
</>
);
}
+50
View File
@@ -0,0 +1,50 @@
import type { Metadata } from 'next';
const BASE_URL = 'https://ayrisai.xyz';
export const metadata: Metadata = {
title: 'AI System Prompts & LLM Templates | ayris.tech',
description:
'Curated AI system prompts and LLM agent templates for ChatGPT, Claude, and Gemini. Covering Next.js architecture, UI/UX design, Python automation, and full-stack development roles.',
keywords: [
'AI system prompts', 'ChatGPT system prompt', 'Claude system prompt',
'LLM agent template', 'AI coding assistant prompt',
'Next.js AI prompt', 'Python AI agent prompt', 'Gemini prompt engineering',
],
alternates: {
canonical: `${BASE_URL}/en/prompts`,
},
openGraph: {
title: 'AI System Prompts & LLM Templates | ayris.tech',
description: 'Battle-tested AI system prompts and LLM templates for ChatGPT, Claude, and Gemini.',
url: `${BASE_URL}/en/prompts`,
type: 'website',
},
};
export default function PromptsLayout({ children }: { children: React.ReactNode }) {
return (
<>
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'CollectionPage',
name: 'AI System Prompts & LLM Templates — ayris.tech',
description:
'Curated AI system prompts for ChatGPT, Claude, and Gemini. For Next.js, Python, UI/UX, and full-stack development.',
url: `${BASE_URL}/en/prompts`,
publisher: {
'@type': 'Person',
name: 'ayris.tech',
url: BASE_URL,
},
}),
}}
/>
{children}
</>
);
}