Metadata — title, description, Open Graph, Twitter Card
Muammodan boshlaylik
Siz do'stingizga sayt havolasini Telegramda yubordingiz. Havola ko'rinishi:
https://mening-saytim.uz/blog/post-1
Shunchaki URL. Hech qanday rasm, sarlavha, tavsif yo'q. Do'stingiz bosadimi? Ehtimol yo'q.
Endi boshqa saytning havolasini yuboring — chiroyli karta ko'rinadi: rasm, sarlavha, qisqa tavsif. Bu — Open Graph metadata.
Ikkinchi muammo — Google da qidirganda sahifangiz qanday ko'rinadi? Sarlavha va tavsif to'g'ri bo'lmasa — odamlar bosmasligi mumkin.
Metadata nima
Metadata — sahifa haqidagi ma'lumot. Foydalanuvchi ko'rmaydi, lekin brauzer, Google, Telegram, Twitter uchun muhim.
<!-- Brauzer tab da ko'rinadi -->
<title>Mening saytim</title>
<!-- Google qidiruv natijasida ko'rinadi -->
<meta name="description" content="Sayt haqida qisqa tavsif" />
<!-- Telegram, WhatsApp da havola yuborilganda -->
<meta property="og:title" content="Sarlavha" />
<meta property="og:image" content="https://..." />
React da bularni <head> ga qo'lda yozish kerak edi. Next.js da buning uchun metadata ob'ekti bor.
Statik metadata
Sahifaning sarlavhasi va tavsifi o'zgarmasa — metadata ob'ektini eksport qilasiz:
// app/about/page.tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Biz haqimizda',
description: 'Kompaniyamiz tarixi va jamoamiz haqida',
}
export default function AboutPage() {
return <h1>Biz haqimizda</h1>
}
Next.js bu ma'lumotlarni avtomatik <head> ga qo'yadi. Siz <head> ga qo'lda hech narsa yozmasangiz ham ishlaydi.
layout.tsx da default metadata
Har bir sahifada alohida yozmaslik uchun root layout.tsx da default metadata belgilanadi. Sahifada o'z metadatasi bo'lsa — u default ni ustidan yozadi:
// app/layout.tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: {
default: 'DevDocs', // sahifada title yo'q bo'lsa
template: '%s | DevDocs', // sahifada title bo'lsa: "Biz haqimizda | DevDocs"
},
description: "O'zbek tilida dasturlash qo'llanmalari",
}
template: '%s | DevDocs' — %s o'rniga sahifaning o'z title si qo'yiladi. Natija: Biz haqimizda | DevDocs.
Sahifada faqat title: 'Biz haqimizda' yozsangiz — avtomatik Biz haqimizda | DevDocs bo'ladi.
Dinamik metadata
Sahifa sarlavhasi URL ga qarab o'zgarsa — generateMetadata funksiyasi ishlatiladi:
// app/blog/[slug]/page.tsx
import { Metadata } from 'next'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}): Promise<Metadata> {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`)
.then(r => r.json())
return {
title: post.title,
description: post.excerpt,
}
}
export default async function BlogPost({ params }) {
// ...
}
/blog/nextjs-kirish ga kirganda — o'sha postning sarlavhasi va tavsifi <head> ga qo'yiladi. Google har bir blog post uchun to'g'ri ma'lumot ko'radi.
Open Graph — ijtimoiy tarmoqlar uchun
Telegram, WhatsApp, Facebook da havola yuborilganda chiroyli karta ko'rsatish uchun Open Graph metadata kerak:
export const metadata: Metadata = {
title: 'Next.js ga kirish',
description: 'Next.js ni noldan o\'rganish',
openGraph: {
title: 'Next.js ga kirish',
description: 'Next.js ni noldan o\'rganish',
url: 'https://devdocs.uz/blog/nextjs',
siteName: 'DevDocs',
images: [
{
url: 'https://devdocs.uz/og/nextjs.png',
width: 1200,
height: 630,
alt: 'Next.js kursi',
},
],
locale: 'uz_UZ',
type: 'article',
},
}
images dagi rasm — havola yuborilganda ko'rinadigan rasm. Standart o'lcham: 1200×630px.
Twitter Card
Twitter (X) Open Graph ni qisman qo'llab-quvvatlaydi, lekin o'zining twitter metadatasi ham bor:
export const metadata: Metadata = {
twitter: {
card: 'summary_large_image', // katta rasm bilan karta
title: 'Next.js ga kirish',
description: 'Next.js ni noldan o\'rganish',
images: ['https://devdocs.uz/og/nextjs.png'],
},
}
card turlari:
summary— kichik rasm, matnsummary_large_image— katta rasm (ko'proq e'tibor tortadi)
opengraph-image.tsx — dinamik OG rasm
Har bir sahifa uchun alohida OG rasm yaratish uchun opengraph-image.tsx fayli:
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const size = { width: 1200, height: 630 }
export default async function OgImage({ params }) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`)
.then(r => r.json())
return new ImageResponse(
(
<div style={{
background: '#0f172a',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 48,
color: 'white',
}}>
{post.title}
</div>
)
)
}
Next.js bu faylni avtomatik topib, OG rasm sifatida ishlatadi. Har bir blog post uchun o'ziga xos rasm — qo'lda rasm yaratmasdan.
Tez-tez uchraydigan xatolar
1. metadata ni Client Component da eksport qilish
// ❌ — Client Component da metadata ishlamaydi
'use client'
export const metadata = { title: 'Sahifa' } // e'tiborga olinmaydi
metadata faqat Server Component da ishlaydi. Agar sahifada 'use client' kerak bo'lsa — metadata ni alohida Server Component da saqlang.
2. OG rasm o'lchami noto'g'ri
// ❌ — kichik rasm, ba'zi platformalarda ko'rinmaydi
images: [{ url: '...', width: 400, height: 200 }]
// ✅ — standart o'lcham
images: [{ url: '...', width: 1200, height: 630 }]
3. generateMetadata da params ni await qilmaslik (Next.js 15+)
// ❌
export async function generateMetadata({ params }) {
const post = await fetch(`/api/${params.slug}`) // xato!
}
// ✅
export async function generateMetadata({ params }) {
const { slug } = await params
const post = await fetch(`/api/${slug}`)
}
Xulosa
Metadata — Google, Telegram, Twitter uchun sahifa haqidagi ma'lumot.
metadata ob'ekti → statik sahifalar uchun
generateMetadata() → dinamik sahifalar uchun (blog, mahsulot)
template: '%s | ...' → barcha sahifalarda bir xil suffix
openGraph → Telegram, Facebook, WhatsApp kartasi
twitter → Twitter/X kartasi
opengraph-image.tsx → dinamik OG rasm yaratish
metadata faqat Server Component da ishlaydi.
Keyingi dars
10-dars: Server Component — nima, qanday ishlaydi, qachon ishlatiladi
Birinchi bo'limni yakunladik. Ikkinchi bo'lim — Rendering Strategiyalari. Server Component ni chuqurroq ko'ramiz.