← HTML: cấu trúc & semantic

Bài 9 · Vận dụng · 26 phút

JSON-LD: dữ liệu có cấu trúc

Biên soạn bởi Nguyễn Anh Tuấn

Structured data với JSON-LD: khai báo Product, NewsArticle, FAQPage, Restaurant… theo schema.org để Google hiện rich result (sao, giá, hỏi đáp). Thư viện công thức theo từng loại hình kinh doanh.

Ở bài HTML cho SEO & metadata, mèo con đã gặp structured data (JSON-LD) ở mức "biết nó tồn tại". Bài này ta đi sâu: viết được JSON-LD cho trang của mình.

Semantic HTML giúp Google đoán nội dung: thấy article + time thì đoán đây là bài viết có ngày đăng. JSON-LD thì khai báo thẳng, không cần đoán: "đây là sản phẩm tên X, giá 250.000đ, 4,8 sao, còn hàng". Khi đã chắc chắn như vậy, Google có thể hiện một rich result - kết quả giàu thông tin hơn:

Không có JSON-LDmeohamhoc.vn › shop › ban-cao-cho-meoBàn cào cho mèo - Mèo ShopBàn cào gỗ chắc chắn cho mèo con màimóng, nhiều kích cỡ.Có Product JSON-LDmeohamhoc.vn › shop › ban-cao-cho-meoBàn cào cho mèo - Mèo Shop★★★★★4,8(256 đánh giá)250.000đCòn hàngBàn cào gỗ chắc chắn cho mèo con mài móng.
Cùng một trang sản phẩm: không có JSON-LD chỉ có tiêu đề + mô tả; có Product JSON-LD thì Google có thể hiện thêm sao, giá, tình trạng còn hàng.
  • Structured data = dữ liệu có cấu trúc, khai báo nội dung trang theo bộ từ vựng schema.org.
  • JSON-LD là cách viết structured data phổ biến nhất - một khối JSON trong thẻ script.
  • Phần thưởng là rich result: sao đánh giá, giá, hỏi đáp, công thức… hiện ngay trên Google.

JSON-LD nằm trong một thẻ <script type="application/ld+json"> và bên trong là một object JSON. Hai khoá đặc biệt luôn có: @context (bộ từ vựng - gần như luôn là https://schema.org) và @type (đây là loại gì). Còn lại là các thuộc tính của loại đó.

course.html · chính meohamhoc.vn dùng kiểu khai báo này cho mỗi khoá (xem src/lib/seo.ts)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Course",
  "name": "HTML: cấu trúc & semantic",
  "description": "Học HTML từ đầu và viết semantic HTML đúng chuẩn.",
  "provider": {
    "@type": "Organization",
    "name": "Mèo Ham Học",
    "url": "https://meohamhoc.vn"
  }
}
</script>

Đọc từ trên xuống: đây là một Course (khoá học), tên là…, mô tả là…, do một Organization tên "Mèo Ham Học" cung cấp. Thuộc tính có thể lồng nhau: provider lại là một object có @type riêng.

Trung thực

JSON-LD không đổi gì trên giao diện người dùng thấy - nó là dữ liệu ẩn cho máy. Và thêm nó không bảo đảm Google sẽ hiện rich result: Google tự quyết dựa trên độ đúng, độ khớp với nội dung trang và chất lượng trang. Đừng khai man (sao ảo, giá sai) - có thể bị phạt.

Loại sinh lời rõ nhất cho một web bán hàng là Product. Hai phần đắt giá: offers (một Offer chứa giá và tình trạng còn hàng) và aggregateRating (điểm trung bình + số lượt đánh giá). Đây chính là thứ dựng nên sao và giá ở hình trên.

product.html · gắn vào trang chi tiết sản phẩm (vd trang bạn đã dựng ở dự án trang bán hàng)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Bàn cào cho mèo",
  "image": "https://meohamhoc.vn/shop/ban-cao.png",
  "description": "Bàn cào gỗ chắc chắn cho mèo con mài móng.",
  "brand": { "@type": "Brand", "name": "Mèo Shop" },
  "offers": {
    "@type": "Offer",
    "price": "250000",
    "priceCurrency": "VND",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.8",
    "reviewCount": "256"
  }
}
</script>
  • Product cần ít nhất name; muốn hiện giá thì thêm offers (price + priceCurrency).
  • availability dùng URL chuẩn schema.org: InStock (còn hàng), OutOfStock (hết hàng).
  • aggregateRating chỉ dùng khi có đánh giá THẬT trên trang - đừng bịa sao.

Nối với dự án trang bán hàng

Ở dự án trang web bán hàng, mỗi sản phẩm đã là một article semantic. Giờ thêm một khối Product JSON-LD cho trang chi tiết sản phẩm là trang đã sẵn sàng cho rich result.

Trang bài báo dùng NewsArticle (hoặc Article cho bài thường). Nó khai báo tựa, ảnh, ngày đăng, tác giả và đơn vị xuất bản - đúng những gì semantic header/time đã gợi, nay nói thẳng cho máy:

article.html · gắn vào trang bài viết (vd dự án trang tin tức)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "NewsArticle",
  "headline": "Mèo con học HTML trong một tuần",
  "image": "https://meohamhoc.vn/tin/meo-hoc-html.png",
  "datePublished": "2026-06-30T08:00:00+07:00",
  "author": { "@type": "Person", "name": "Mèo Biên Tập" },
  "publisher": {
    "@type": "Organization",
    "name": "Mèo Ham Học",
    "logo": { "@type": "ImageObject", "url": "https://meohamhoc.vn/logo.png" }
  }
}
</script>

Một loại đi kèm rất hữu ích cho mọi trang con là BreadcrumbList - đường dẫn phân cấp. Nó giúp Google hiện đường dẫn đẹp (Trang chủ › Tin tức › Bài) thay cho URL trần:

breadcrumb.html · đường dẫn phân cấp cho một trang con

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Trang chủ", "item": "https://meohamhoc.vn" },
    { "@type": "ListItem", "position": 2, "name": "Tin tức", "item": "https://meohamhoc.vn/tin" },
    { "@type": "ListItem", "position": 3, "name": "Mèo con học HTML" }
  ]
}
</script>

Nối với dự án trang tin tức

Trang tin tức đã có article, time datetime và breadcrumb dạng hiển thị. Thêm NewsArticle + BreadcrumbList JSON-LD là phần khai báo cho máy đã trọn vẹn.

Mỗi loại hình kinh doanh có một @type riêng, mở một kiểu rich result riêng. Hai ví dụ đầy đủ thường gặp - nhà hàng/cửa hàngtrang hỏi đáp:

restaurant.html · quán ăn / cửa hàng địa phương (Restaurant là một dạng LocalBusiness)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Restaurant",
  "name": "Quán Cá Mèo",
  "servesCuisine": "Hải sản",
  "priceRange": "$$",
  "telephone": "+84-28-1234-5678",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "12 Đường Cá",
    "addressLocality": "TP.HCM",
    "addressCountry": "VN"
  }
}
</script>

faq.html · khối hỏi đáp - Google có thể hiện ngay câu hỏi/đáp dưới kết quả

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Giao hàng mất bao lâu?",
      "acceptedAnswer": { "@type": "Answer", "text": "Thường 2-3 ngày trong nội thành." }
    },
    {
      "@type": "Question",
      "name": "Có đổi trả không?",
      "acceptedAnswer": { "@type": "Answer", "text": "Đổi trả trong 7 ngày nếu còn nguyên hộp." }
    }
  ]
}
</script>

Cùng một khuôn, chỉ đổi @type và thuộc tính. Vài loại phổ biến khác:

  • LocalBusiness: cửa hàng/dịch vụ địa phương - address, telephone, openingHours → hiện địa chỉ, giờ mở cửa.
  • JobPosting: tin tuyển dụng - title, hiringOrganization, baseSalary, jobLocation → lên Google Jobs.
  • Recipe: công thức nấu ăn - recipeIngredient, recipeInstructions, cookTime → thẻ công thức có ảnh, thời gian.
  • Event: sự kiện - name, startDate, location → hiện ngày giờ, địa điểm trong kết quả.

Mẹo chọn @type

Không chắc dùng loại nào? Mở schema.org, tìm loại gần nhất với trang của mèo con (Product, Article, Event…), rồi xem trang Google Search Central liệt kê đúng những thuộc tính nào là bắt buộc để được rich result. Đừng nhồi mọi thuộc tính - chỉ cần đúng và khớp nội dung.

Một khối JSON-LD sai một dấu phẩy là hỏng cả khối. Đừng tin nó đúng vì "trông có vẻ đúng" - hãy kiểm bằng công cụ. Google có Rich Results Test (search.google.com/test/rich-results): dán URL hoặc dán thẳng đoạn HTML, nó báo loại nào hợp lệ, thiếu thuộc tính gì, sai ở đâu.

  • Rich Results Test: kiểm trang có đủ điều kiện rich result chưa, báo lỗi cụ thể.
  • Schema Markup Validator (validator.schema.org): kiểm cú pháp schema.org tổng quát.
  • Lỗi hay gặp: thiếu thuộc tính bắt buộc, sai định dạng ngày, dữ liệu không khớp nội dung trang.

Trung thực

Công cụ báo "hợp lệ" nghĩa là đủ điều kiện, không phải "chắc chắn sẽ hiện". Google vẫn tự quyết khi xếp kết quả. Việc của mèo con: viết đúng, khớp nội dung thật, rồi để Google làm phần còn lại.

Đồ nghề mang về

Mèo Ham Học gói sẵn một cheatsheet (bản tra nhanh: landmark semantic + head SEO + thư viện công thức JSON-LD theo loại hình) kèm một skill cho Claude Code (semantic-html-seo). Tải về để tra khi dựng trang, hoặc cài cho AI tự viết HTML theo chuẩn này: 📦 Tải cheatsheet + skill (.zip)

Bước tiếp theo

Mèo con đã biết khai báo trang cho máy hiểu. Quay lại phần tốc độ: cách đưa JavaScript vào trang sao cho không làm trang chậm - Thẻ script: defer & async.

Câu hỏi thường gặp

meta description là một câu mô tả cho NGƯỜI đọc trên kết quả tìm kiếm. JSON-LD là dữ liệu cho MÁY đọc: nó khai báo rõ "đây là sản phẩm giá 250.000đ, 4,8 sao" để Google có thể hiện thêm sao, giá, tình trạng còn hàng (rich result). Một cái là lời mô tả, một cái là dữ liệu có cấu trúc.

Không. Google TỰ QUYẾT có hiện rich result hay không. JSON-LD đúng cú pháp là điều kiện cần, nhưng dữ liệu phải KHỚP với nội dung hiện trên trang, trang phải đủ chất lượng, và đúng loại được Google hỗ trợ. Khai man (sao ảo, giá sai) có thể bị phạt.

Trong một thẻ <script type="application/ld+json">. Đặt trong head hay cuối body đều được; một trang có thể có nhiều khối (vd Product + BreadcrumbList + FAQPage). Vì là JSON nên nó không phụ thuộc vị trí như thẻ hiển thị.

Không. Nó nằm trong thẻ script, người dùng không thấy gì khác trên trang. Chính vì ẩn nên nó phải KHỚP nội dung thật: giá trong JSON-LD phải đúng giá trên trang, sao phải là đánh giá có thật. Đây là dữ liệu cho máy, không phải chỗ để nói khác đi.

@context trỏ tới bộ từ vựng đang dùng - gần như luôn là "https://schema.org" (kho từ vựng chung mà Google, Bing… đều hiểu). @type cho biết đây là loại gì: Product, NewsArticle, Restaurant… @type quyết định Google trông đợi những thuộc tính nào.

Semantic là NỀN: chỉ riêng article/header/time/h1 đúng đã giúp Google hiểu kha khá. JSON-LD là lớp THÊM để mở rich result cho những loại trang phù hợp (sản phẩm, công thức, sự kiện, hỏi đáp…). Làm tốt semantic trước; thêm JSON-LD khi trang thuộc loại được hưởng lợi.

Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.

Tick những điều em tự tin làm được sau khi học bài này. 0/6

Trả lời vài câu để chắc rằng em đã nắm bài.

Câu 1/3 Điểm: 0

Structured data (JSON-LD) trong trang chủ yếu để làm gì?

Bài tập về nhà

  1. 1

    Product cho trang shop

    Viết khối JSON-LD Product cho một sản phẩm của mèo con (vd món đã dựng ở dự án trang bán hàng): có name, image, description và offers (price, priceCurrency "VND", availability).

    ✅ Hoàn thành khi: Có <script type="application/ld+json"> với "@type":"Product" và một Offer đủ price + priceCurrency + availability.

  2. 2

    Thêm sao đánh giá

    Bổ sung aggregateRating vào Product ở trên: ratingValue và reviewCount.

    ✅ Hoàn thành khi: Product có thuộc tính aggregateRating với "@type":"AggregateRating", ratingValue và reviewCount.

  3. 3

    NewsArticle cho bài viết

    Viết JSON-LD NewsArticle cho một bài (vd trang tin tức đã dựng): headline, image, datePublished, author và publisher.

    ✅ Hoàn thành khi: Có "@type":"NewsArticle" với headline, datePublished (định dạng ISO), author và publisher.

  4. 4

    FAQPage cho trang shop

    Viết JSON-LD FAQPage với 2 câu hỏi - đáp thường gặp (vd giao hàng, đổi trả).

    ✅ Hoàn thành khi: Có "@type":"FAQPage" với mainEntity gồm 2 Question, mỗi Question có acceptedAnswer.

  5. 5

    BreadcrumbList 3 cấp

    Viết JSON-LD BreadcrumbList cho đường dẫn Trang chủ › Danh mục › Trang hiện tại.

    ✅ Hoàn thành khi: Có "@type":"BreadcrumbList" với 3 ListItem, mỗi cái có position và name (item cho 2 cấp đầu).

  6. 6

    Kiểm chứng bằng công cụ

    Dán một khối JSON-LD ở trên vào Google Rich Results Test (search.google.com/test/rich-results), sửa cho tới khi không còn lỗi. Viết 1-2 câu mèo con học được gì từ báo lỗi.

    ✅ Hoàn thành khi: Công cụ báo khối JSON-LD hợp lệ (0 lỗi), kèm 1-2 câu ghi lại điều rút ra.