Урок 08

Метаданные и SEO

Next.js предоставляет API для метаданных и SEO.

Статические метаданные

import type { Metadata } from 'next';

export const metadata: Metadata = {
    title: 'Мой сайт',
    description: 'Описание сайта',
    keywords: ['nextjs', 'react'],
};

Работает в layout.tsx и page.tsx.

Шаблоны

// app/layout.tsx
export const metadata: Metadata = {
    title: {
        template: '%s | Мой сайт',
        default: 'Мой сайт',
    },
};

Страница с title: 'О нас' даст «О нас | Мой сайт».

Динамические

export async function generateMetadata({ params }): Promise<Metadata> {
    const { id } = await params;
    const post = await getPost(id);

    return {
        title: post.title,
        description: post.excerpt,
    };
}

Open Graph

Для соцсетей:

export const metadata: Metadata = {
    openGraph: {
        title: 'Мой сайт',
        description: 'Описание',
        url: 'https://example.com',
        siteName: 'Мой сайт',
        images: [
            {
                url: 'https://example.com/og.jpg',
                width: 1200,
                height: 630,
                alt: 'Превью',
            },
        ],
        locale: 'ru_RU',
        type: 'website',
    },
};

Twitter Cards

export const metadata: Metadata = {
    twitter: {
        card: 'summary_large_image',
        title: 'Мой сайт',
        description: 'Описание',
        images: ['https://example.com/twitter.jpg'],
    },
};

Canonical URL

export const metadata: Metadata = {
    alternates: {
        canonical: 'https://example.com/page',
    },
};

Robots

export const metadata: Metadata = {
    robots: {
        index: true,
        follow: true,
        googleBot: {
            index: true,
            follow: true,
        },
    },
};

Закрыть от индексации:

export const metadata: Metadata = {
    robots: { index: false, follow: false },
};

Иконки

app/icon.png и app/apple-icon.png автоматически подхватываются.

Или через metadata:

export const metadata: Metadata = {
    icons: {
        icon: '/favicon.ico',
        apple: '/apple-icon.png',
    },
};

Sitemap

app/sitemap.ts:

import type { MetadataRoute } from 'next';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
    const posts = await getPosts();

    return [
        {
            url: 'https://example.com',
            lastModified: new Date(),
            priority: 1,
        },
        ...posts.map((post) => ({
            url: `https://example.com/posts/${post.slug}`,
            lastModified: post.updatedAt,
            priority: 0.8,
        })),
    ];
}

Доступен на /sitemap.xml.

Robots.txt

app/robots.ts:

import type { MetadataRoute } from 'next';

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

Доступен на /robots.txt.

JSON-LD

export default function Page() {
    const jsonLd = {
        '@context': 'https://schema.org',
        '@type': 'Article',
        headline: 'Заголовок',
        author: { '@type': 'Person', name: 'Алиса' },
    };

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

Viewport

export const viewport = {
    width: 'device-width',
    initialScale: 1,
    maximumScale: 1,
};

Итоги

  • metadata для статических
  • generateMetadata для динамических
  • Open Graph для соцсетей
  • sitemap.ts и robots.ts
  • JSON-LD через script
  • Иконки автоматически