SEPEHR.SYS

Starting SEPEHR.SYS…

Loading

سپهر محسنی

مهندس نرم‌افزار فول‌استک و هوش مصنوعی

NOTES.TXT ×

بهینه‌سازی سئو در Next.js: راهنمای کامل برای کسب رتبهٔ بالاتر

6′ · ۲۷ دی ۱۴۰۴

سئو برای موفقیت هر وب‌سایتی حیاتی است. Next.js ابزارهای قدرتمندی برای ساخت اپلیکیشن‌های سازگار با سئو در اختیارتان می‌گذارد. این راهنما همه‌چیز را — از متادیتای پایه تا تکنیک‌های پیشرفتهٔ بهینه‌سازی — پوشش می‌دهد.

Metadata API در Next.js 15

متادیتای استاتیک

// app/page.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Home | My Website',
  description: 'Welcome to my website. We offer the best products and services.',
  keywords: ['products', 'services', 'quality'],
  authors: [{ name: 'John Doe', url: 'https://johndoe.com' }],
  creator: 'John Doe',
  publisher: 'My Company',
  robots: {
    index: true,
    follow: true,
    googleBot: {
      index: true,
      follow: true,
      'max-video-preview': -1,
      'max-image-preview': 'large',
      'max-snippet': -1,
    },
  },
  openGraph: {
    type: 'website',
    locale: 'en_US',
    url: 'https://mywebsite.com',
    siteName: 'My Website',
    title: 'Home | My Website',
    description: 'Welcome to my website',
    images: [
      {
        url: 'https://mywebsite.com/og-image.jpg',
        width: 1200,
        height: 630,
        alt: 'My Website',
      },
    ],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Home | My Website',
    description: 'Welcome to my website',
    creator: '@johndoe',
    images: ['https://mywebsite.com/twitter-image.jpg'],
  },
  alternates: {
    canonical: 'https://mywebsite.com',
    languages: {
      'en-US': 'https://mywebsite.com/en',
      'fa-IR': 'https://mywebsite.com/fa',
    },
  },
};

export default function HomePage() {
  return <main>...</main>;
}

<Callout type="info"> برای اینکه پیش‌نمایش بهتری هنگام اشتراک‌گذاری در شبکه‌های اجتماعی داشته باشید، همیشه متادیتای Open Graph و Twitter Card را اضافه کنید. </Callout>

متادیتای داینامیک

// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';
import { getPostBySlug } from '@/lib/posts';

interface Props {
  params: { slug: string };
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = await getPostBySlug(params.slug);

  if (!post) {
    return {
      title: 'Post Not Found',
    };
  }

  return {
    title: `${post.title} | My Blog`,
    description: post.excerpt,
    authors: [{ name: post.author.name }],
    openGraph: {
      type: 'article',
      title: post.title,
      description: post.excerpt,
      url: `https://mywebsite.com/blog/${params.slug}`,
      publishedTime: post.publishedAt,
      modifiedTime: post.updatedAt,
      authors: [post.author.name],
      images: [
        {
          url: post.coverImage,
          width: 1200,
          height: 630,
          alt: post.title,
        },
      ],
      tags: post.tags,
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage],
    },
  };
}

قالب متادیتا

// app/layout.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  metadataBase: new URL('https://mywebsite.com'),
  title: {
    default: 'My Website',
    template: '%s | My Website', // Dynamic pages will use this template
  },
  description: 'Default description for my website',
  openGraph: {
    type: 'website',
    siteName: 'My Website',
  },
};

دادهٔ ساخت‌یافته (JSON-LD)

اسکیمای Organization

// components/StructuredData.tsx
export function OrganizationSchema() {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Organization',
    name: 'My Company',
    url: 'https://mywebsite.com',
    logo: 'https://mywebsite.com/logo.png',
    sameAs: [
      'https://twitter.com/mycompany',
      'https://linkedin.com/company/mycompany',
      'https://github.com/mycompany',
    ],
    contactPoint: {
      '@type': 'ContactPoint',
      telephone: '+1-555-555-5555',
      contactType: 'customer service',
      availableLanguage: ['English', 'Persian'],
    },
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

اسکیمای Article

// components/ArticleSchema.tsx
interface ArticleSchemaProps {
  title: string;
  description: string;
  image: string;
  datePublished: string;
  dateModified: string;
  author: {
    name: string;
    url: string;
  };
  url: string;
}

export function ArticleSchema({
  title,
  description,
  image,
  datePublished,
  dateModified,
  author,
  url,
}: ArticleSchemaProps) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: title,
    description: description,
    image: image,
    datePublished: datePublished,
    dateModified: dateModified,
    author: {
      '@type': 'Person',
      name: author.name,
      url: author.url,
    },
    publisher: {
      '@type': 'Organization',
      name: 'My Website',
      logo: {
        '@type': 'ImageObject',
        url: 'https://mywebsite.com/logo.png',
      },
    },
    mainEntityOfPage: {
      '@type': 'WebPage',
      '@id': url,
    },
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

<Note title="نکتهٔ اسکیما"> برای اعتبارسنجی دادهٔ ساخت‌یافتهٔ خود از ابزار Rich Results Test گوگل استفاده کنید: https://search.google.com/test/rich-results </Note>

اسکیمای Product برای فروشگاه اینترنتی

export function ProductSchema({ product }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    description: product.description,
    image: product.images,
    sku: product.sku,
    brand: {
      '@type': 'Brand',
      name: product.brand,
    },
    offers: {
      '@type': 'Offer',
      url: `https://mywebsite.com/products/${product.slug}`,
      priceCurrency: 'USD',
      price: product.price,
      availability: product.inStock
        ? 'https://schema.org/InStock'
        : 'https://schema.org/OutOfStock',
      seller: {
        '@type': 'Organization',
        name: 'My Store',
      },
    },
    aggregateRating: product.reviews.length > 0 ? {
      '@type': 'AggregateRating',
      ratingValue: product.averageRating,
      reviewCount: product.reviews.length,
    } : undefined,
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

تولید نقشهٔ سایت (Sitemap)

نقشهٔ سایت داینامیک

// app/sitemap.ts
import { MetadataRoute } from 'next';
import { getAllPosts } from '@/lib/posts';
import { getAllProducts } from '@/lib/products';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const baseUrl = 'https://mywebsite.com';

  // Static pages
  const staticPages = [
    {
      url: baseUrl,
      lastModified: new Date(),
      changeFrequency: 'daily' as const,
      priority: 1,
    },
    {
      url: `${baseUrl}/about`,
      lastModified: new Date(),
      changeFrequency: 'monthly' as const,
      priority: 0.8,
    },
    {
      url: `${baseUrl}/contact`,
      lastModified: new Date(),
      changeFrequency: 'monthly' as const,
      priority: 0.8,
    },
  ];

  // Blog posts
  const posts = await getAllPosts();
  const blogPages = posts.map((post) => ({
    url: `${baseUrl}/blog/${post.slug}`,
    lastModified: new Date(post.updatedAt),
    changeFrequency: 'weekly' as const,
    priority: 0.7,
  }));

  // Products
  const products = await getAllProducts();
  const productPages = products.map((product) => ({
    url: `${baseUrl}/products/${product.slug}`,
    lastModified: new Date(product.updatedAt),
    changeFrequency: 'daily' as const,
    priority: 0.9,
  }));

  return [...staticPages, ...blogPages, ...productPages];
}

Robots.txt

// app/robots.ts
import { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/api/', '/admin/', '/private/'],
      },
      {
        userAgent: 'Googlebot',
        allow: '/',
        disallow: '/api/',
      },
    ],
    sitemap: 'https://mywebsite.com/sitemap.xml',
  };
}

بهینه‌سازی Core Web Vitals

بهینه‌سازی تصاویر

import Image from 'next/image';

// Optimized image component
export function OptimizedImage({ src, alt, priority = false }) {
  return (
    <Image
      src={src}
      alt={alt}
      width={800}
      height={600}
      priority={priority} // Set true for above-the-fold images
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
      sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
      className="object-cover"
    />
  );
}

// Hero image with priority loading
export function HeroSection() {
  return (
    <section>
      <Image
        src="/hero.jpg"
        alt="Hero image"
        fill
        priority // Critical for LCP
        sizes="100vw"
        className="object-cover"
      />
    </section>
  );
}

بهینه‌سازی فونت‌ها

// app/layout.tsx
import { Inter, Vazirmatn } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
});

const vazirmatn = Vazirmatn({
  subsets: ['arabic'],
  display: 'swap',
  variable: '--font-vazirmatn',
});

export default function RootLayout({ children }) {
  return (
    <html className={`${inter.variable} ${vazirmatn.variable}`}>
      <body>{children}</body>
    </html>
  );
}

بهینه‌سازی اسکریپت‌ها

import Script from 'next/script';

export function Analytics() {
  return (
    <>
      {/* Load after page is interactive */}
      <Script
        src="https://www.googletagmanager.com/gtag/js?id=GA_ID"
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', 'GA_ID');
        `}
      </Script>

      {/* Load when browser is idle */}
      <Script
        src="https://third-party-script.js"
        strategy="lazyOnload"
      />
    </>
  );
}

سئو برای سایت‌های چندزبانه

تگ‌های Hreflang

// app/[locale]/layout.tsx
import type { Metadata } from 'next';

export async function generateMetadata({ params }): Promise<Metadata> {
  const { locale } = params;
  
  return {
    alternates: {
      canonical: `https://mywebsite.com/${locale}`,
      languages: {
        'en': 'https://mywebsite.com/en',
        'fa': 'https://mywebsite.com/fa',
        'x-default': 'https://mywebsite.com/en',
      },
    },
  };
}

نقشهٔ سایت بومی‌سازی‌شده

// app/sitemap.ts
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const locales = ['en', 'fa'];
  const baseUrl = 'https://mywebsite.com';

  const pages = ['', '/about', '/contact', '/blog'];

  return pages.flatMap((page) =>
    locales.map((locale) => ({
      url: `${baseUrl}/${locale}${page}`,
      lastModified: new Date(),
      alternates: {
        languages: Object.fromEntries(
          locales.map((l) => [l, `${baseUrl}/${l}${page}`])
        ),
      },
    }))
  );
}

مانیتورینگ عملکرد

ردیابی Web Vitals

// app/components/WebVitals.tsx
'use client';

import { useReportWebVitals } from 'next/web-vitals';

export function WebVitals() {
  useReportWebVitals((metric) => {
    // Send to analytics
    const body = JSON.stringify({
      name: metric.name,
      value: metric.value,
      rating: metric.rating,
      delta: metric.delta,
      id: metric.id,
    });

    // Use sendBeacon for reliability
    if (navigator.sendBeacon) {
      navigator.sendBeacon('/api/analytics/vitals', body);
    } else {
      fetch('/api/analytics/vitals', {
        body,
        method: 'POST',
        keepalive: true,
      });
    }
  });

  return null;
}

جمع‌بندی

سئو در Next.js یعنی ترکیب بهینه‌سازی فنی با محتوای باکیفیت. با پیاده‌سازی درست متادیتا، دادهٔ ساخت‌یافته، نقشهٔ سایت و بهینه‌سازی Core Web Vitals می‌توانید رتبهٔ خود را در موتورهای جستجو به‌شکل چشمگیری بهبود دهید.

نکات کلیدی:

  • برای داشتن متاتگ‌های جامع از Metadata API استفاده کنید
  • برای نمایش نتایج غنی در جستجو، دادهٔ ساخت‌یافته پیاده‌سازی کنید
  • برای همهٔ محتوا نقشهٔ سایت داینامیک تولید کنید
  • تصاویر، فونت‌ها و اسکریپت‌ها را برای Core Web Vitals بهینه کنید
  • چندزبانگی را با تگ‌های hreflang صحیح مدیریت کنید

این یادداشت ترجمهٔ فارسی نوشتهٔ خودم است — نسخهٔ اصلی (انگلیسی) در dev.to