2026-03-206 phút đọcVI
Định dạng Case Study
Multi-Schema Strategy
Mở đầu
Một page trên web không chỉ chứa một loại entity. Blog post page của leduykhuong.com vừa là BlogPosting (bài viết), vừa có BreadcrumbList (navigation path). Homepage vừa là WebSite, vừa có thông tin về Person (tác giả). Mỗi page cần một chiến lược kết hợp nhiều schemas để Google hiểu đầy đủ context.
Mục tiêu: Hiểu cách leduykhuong.com dùng nhiều schema types trên từng loại page, cách tổ chức schema per-page, và khi nào nên thêm schema mới.
Schema Map — Toàn bộ site
leduykhuong.com có 4 schema types, phân bổ theo page:
| Page | Schemas | Helper functions |
|---|---|---|
| Homepage | WebSite + SearchAction | getWebSiteJsonLd() |
| About | Person | getPersonJsonLd() |
| Blog post | BlogPosting + BreadcrumbList | getBlogPostJsonLd() + getBreadcrumbJsonLd() |
| Blog landing | (chưa có) | — |
| Topic/Pillar | (chưa có) | — |
Tổng cộng: 4 helper functions trong lib/seo.ts, mỗi function return một JSON-LD object.
Schema Types
WebSite, Person, BlogPosting, BreadcrumbList
Helper Functions
In lib/seo.ts
Page Types
Homepage, About, Blog Post, Landing, Topic
WebSite Schema — Homepage
// lib/seo.ts
export function getWebSiteJsonLd() {
return {
"@context": "https://schema.org",
"@type": "WebSite",
name: SITE_CONFIG.siteName,
url: SITE_CONFIG.baseUrl,
description: SITE_CONFIG.description,
potentialAction: {
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: `${SITE_CONFIG.baseUrl}/blog/search?q={search_term_string}`,
},
"query-input": "required name=search_term_string",
},
};
}Phân tích:
@type: "WebSite"— Nói Google: "Đây là trang chủ của website."name+url— Thông tin cơ bản, Google dùng cho Knowledge Panel.potentialAction: SearchAction— Tính năng đặc biệt: nói Google rằng site có search. Google CÓ THỂ hiển thị Sitelinks Searchbox — ô tìm kiếm trực tiếp trên search results.
Sitelinks Searchbox
Khi Google hiển thị Sitelinks Searchbox:
Le Duy Khuong — leduykhuong.com
Engineering Leader × AI/Agentic Systems Builder
┌─────────────────────────────────┐
│ Search leduykhuong.com │
└─────────────────────────────────┘
User có thể search trực tiếp từ Google results mà không cần vào site trước. urlTemplate chỉ định URL pattern: khi user search "agentic", Google redirect tới leduykhuong.com/blog/search?q=agentic.
Yêu cầu: Site phải có search functionality thực sự tại URL pattern khai báo. leduykhuong.com có Pagefind search tại /blog/search.
Person Schema — About Page
// lib/seo.ts
export function getPersonJsonLd() {
return {
"@context": "https://schema.org",
"@type": "Person",
name: SITE_CONFIG.author.name,
jobTitle: SITE_CONFIG.author.jobTitle,
url: SITE_CONFIG.author.url,
sameAs: [SITE_CONFIG.author.linkedin, SITE_CONFIG.author.github],
};
}Phân tích:
jobTitle— "Head of Digital & AI Transformation". Google dùng cho Knowledge Panel.sameAs— Array URLs profile trên các platform khác (LinkedIn, GitHub). Google dùng để connect entities: "Le Duy Khuong trên leduykhuong.com = Le Duy Khuong trên LinkedIn." Điều này strengthen author authority.
sameAs best practices:
- Chỉ khai báo profiles anh thực sự sở hữu
- LinkedIn profile nên public
- Thứ tự không quan trọng
- Có thể thêm: Twitter/X, Google Scholar, Orcid, Medium...
BreadcrumbList Schema — Blog Posts
// lib/seo.ts
export function getBreadcrumbJsonLd(
items: { name: string; url: string }[]
) {
return {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((item, i) => ({
"@type": "ListItem",
position: i + 1,
name: item.name,
item: item.url,
})),
};
}Sử dụng trong blog post:
<JsonLd data={getBreadcrumbJsonLd([
{ name: "Home", url: SITE_CONFIG.baseUrl },
{ name: "Blog", url: `${SITE_CONFIG.baseUrl}/blog` },
{ name: post.title, url: `${SITE_CONFIG.baseUrl}/blog/${post.slug}` },
])} />Google rich result:
leduykhuong.com > Blog > Learning in Public
Thay vì URL dài leduykhuong.com/vi/blog/learning-in-public, Google hiển thị breadcrumb trail readable.
Key rules:
positionbắt đầu từ 1 (không phải 0)- Item cuối cùng = page hiện tại
- Mỗi item phải có
namevàitem(URL) - URL phải absolute
Tại sao mỗi page cần strategy khác?
Mỗi loại page có primary purpose khác nhau:
| Page | Primary entity | Secondary entities | Lý do |
|---|---|---|---|
| Homepage | WebSite | (SearchAction embedded) | Google cần biết đây là entry point |
| About | Person | — | Focus vào author identity |
| Blog post | BlogPosting | BreadcrumbList | Content + navigation context |
| Blog landing | (none yet) | — | Listing page, ít structured data cần |
Principle: Schema nên phản ánh purpose của page, không phải liệt kê mọi entity có mặt. Homepage KHÔNG cần BlogPosting schema dù nó hiển thị recent posts — vì purpose của homepage là giới thiệu website, không phải display blog content.
Multiple <script> Tags — Cách Google xử lý
Mỗi JsonLd component render một <script> riêng:
<!-- Blog post page -->
<script type="application/ld+json">{"@type":"BlogPosting",...}</script>
<script type="application/ld+json">{"@type":"BreadcrumbList",...}</script>Cách Google đọc:
- Parse TẤT CẢ
<script type="application/ld+json">blocks trên page - Extract entities từ mỗi block
- Combine entities → hiểu toàn bộ page context
- Quyết định hiển thị rich results nào
Alternative: @graph array — Có thể gộp nhiều entities vào 1 script:
{
"@context": "https://schema.org",
"@graph": [
{ "@type": "BlogPosting", "headline": "..." },
{ "@type": "BreadcrumbList", "itemListElement": [...] }
]
}Cả hai approaches đều valid. leduykhuong.com chọn multiple scripts vì:
- Dễ maintain (mỗi component độc lập)
- Dễ debug (grep từng type)
- Dễ add/remove per page
Schemas chưa implement — Cơ hội mở rộng
Một số schema types có thể thêm trong tương lai:
CollectionPage — Blog landing
{
"@type": "CollectionPage",
"name": "Blog",
"description": "Tất cả bài viết từ Le Duy Khuong",
"url": "https://leduykhuong.com/blog",
"mainEntity": {
"@type": "ItemList",
"numberOfItems": 125,
"itemListElement": [...]
}
}Giúp Google hiểu: "Trang này là tập hợp bài viết, không phải một bài riêng."
FAQPage — Nếu có Q&A content
{
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Agentic AI là gì?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Agentic AI là hệ thống AI..."
}
}
]
}Google hiển thị FAQ expandable trực tiếp trên search results — tăng real estate đáng kể.
HowTo — Tutorial posts
Nếu blog post là tutorial/hướng dẫn, HowTo schema cho Google hiển thị steps:
Step 1: Install dependencies
Step 2: Configure environment
Step 3: Deploy
Design Principle — Schema per Page Type
Khi quyết định thêm schema, follow pattern:
1. Xác định page type (homepage, about, blog post, listing...)
2. Chọn primary schema (1 type chính)
3. Thêm supplementary schemas (breadcrumb, etc.) nếu cần
4. Tạo helper function trong lib/seo.ts
5. Inject qua <JsonLd> trong page component
Anti-patterns:
- Thêm schema không liên quan tới page content
- Khai báo required fields sai (Google ignore hoặc penalize)
- Duplicate schemas trên cùng page (2 BlogPosting trên 1 page)
Thực hành
Bài tập 1: Map schema usage
cd ACE-component/ACE-leduykhuong-site
# Tìm tất cả nơi dùng JsonLd
grep -rn "JsonLd" app/ --include="*.tsx"
# Tìm tất cả schema helpers
grep -n "export function get.*JsonLd" lib/seo.tsCâu hỏi: Có bao nhiêu page types dùng JsonLd? Có page nào dùng 2+ schemas?
Bài tập 2: Check homepage schema
npm run build
grep 'application/ld+json' out/index.htmlCâu hỏi: Homepage có schema WebSite không? SearchAction urlTemplate trỏ đúng URL không?
Bài tập 3: Thiết kế schema cho Topic page
Topic page (ví dụ: /topics/agentic-ai) hiển thị danh sách posts theo topic. Schema type nào phù hợp nhất? Thử sketch JSON-LD object cho page này.
Tóm tắt
- Multi-schema — Mỗi page có thể chứa nhiều schema types (BlogPosting + BreadcrumbList)
- WebSite + SearchAction — Homepage schema, có thể trigger Sitelinks Searchbox
- Person + sameAs — About page, connect author identity across platforms
- BreadcrumbList — Navigation context, Google hiển thị readable path
- Multiple
<script>tags — Google parse tất cả blocks, dễ maintain hơn@graph - Schema per page type — Chọn primary schema theo page purpose, thêm supplementary khi cần
Bài tiếp theo
Bài 8: Debugging & Extending Structured Data — Cách dùng Rich Results Test, Schema Markup Validator, và Chrome DevTools để validate JSON-LD. Plus: quy trình thêm schema type mới cho leduykhuong.com.