Pillar/cluster is not an SEO trick — it is just a way of organizing content that mirrors how Google evaluates topical authority. When done right, your pillar page ranks for the broad term and each cluster page ranks for a long-tail variation, and they all link to each other in a sensible way. The trick is encoding the relationships in your content schema, not in your head.
Background
A “pillar page” is a broad page covering a topic at depth (e.g. “Setting up a content site”). A “cluster page” is a narrower page covering one subtopic in detail (e.g. “Submitting your sitemap to Google”). Each cluster links up to the pillar; the pillar links down to each cluster. Google reads the link graph and concludes “this site has depth on this topic”.
How to tell
- You can name your top 5-10 topic clusters in one breath.
- Each pillar page has 5-15 cluster pages linking to it.
- No cluster page is more than 2 clicks from the homepage.
- Internal anchor text uses the actual target keyword, not “click here”.
- A new article naturally fits into an existing pillar without manual reshuffling.
Quick verdict
Set up pillar/cluster as data — a pillar field in frontmatter and a script that builds the link graph. Two-way linking, descriptive anchors, and orphan detection are what make it work.
Before you start
- You have at least 20-30 published articles to organize.
- Content schema can be extended to add a
pillarfield. - You can run a small Node script to audit links.
Step by step
- Add a
pillarfield to frontmatter. Extend the schema:
// src/content/config.ts
schema: z.object({
// ...
pillar: z.string().optional(), // slug of the pillar this article belongs to
isPillar: z.boolean().default(false),
}),
Cluster article:
---
title: "How to submit a sitemap to Search Console"
urlSlug: "submit-sitemap-search-console"
pillar: "submit-website-to-google"
isPillar: false
---
Pillar article:
---
title: "Submitting a new site to Google in 2026"
urlSlug: "submit-new-site-to-google-2026"
isPillar: true
---
-
Brainstorm 5-10 pillar topics. Each needs at least 10-20 candidate clusters or it is too narrow. If you have more than 10 pillars, you are too wide.
-
Write cluster articles first. A pillar drafted before the clusters comes out abstract. Ship 5-7 clusters per pillar, then write the pillar — the pillar will be richer and more honest.
-
Generate the pillar’s “child” list automatically. In the pillar’s layout:
---
import { getCollection } from 'astro:content';
const { lang, urlSlug } = Astro.props.article.data;
const clusters = (await getCollection('articles', (a) =>
a.data.lang === lang && a.data.pillar === urlSlug && !a.data.isPillar
)).sort((a, b) => a.data.title.localeCompare(b.data.title));
---
<h2>In this guide</h2>
<ul>
{clusters.map((c) => (
<li>
<a href={`/${lang}/articles/${c.data.urlSlug}/`}>{c.data.title}</a>
<p>{c.data.description}</p>
</li>
))}
</ul>
- Back-link clusters to pillar automatically. In the cluster’s layout, render a “back to pillar” block:
---
const { lang, pillar } = Astro.props.article.data;
const p = pillar
? (await getEntry('articles', `${lang}/${pillarPath}/${pillar}`))
: null;
---
{p && (
<aside class="pillar-up">
Part of: <a href={`/${lang}/articles/${p.data.urlSlug}/`}>{p.data.title}</a>
</aside>
)}
-
Use descriptive anchor text in both directions. Pillar → cluster anchors describe the cluster’s topic; cluster → pillar anchors describe the pillar’s topic. Avoid “click here” or the literal slug.
-
Run a pillar/cluster audit script. Find weak pillars and orphan clusters:
// scripts/audit-pillars.mjs
import { readdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import matter from 'gray-matter';
const byPillar = new Map();
const pillars = new Set();
function walk(dir) {
for (const f of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, f.name);
if (f.isDirectory()) walk(full);
else if (f.name.endsWith('.mdx')) {
const { data } = matter(readFileSync(full, 'utf8'));
if (data.isPillar) pillars.add(data.urlSlug);
else if (data.pillar) {
if (!byPillar.has(data.pillar)) byPillar.set(data.pillar, []);
byPillar.get(data.pillar).push(data.urlSlug);
}
}
}
}
walk('src/content/articles');
for (const p of pillars) {
const count = (byPillar.get(p) || []).length;
if (count < 4) console.warn(`WEAK PILLAR (${count}): ${p}`);
}
for (const [pillar, kids] of byPillar) {
if (!pillars.has(pillar)) {
console.warn(`ORPHAN CLUSTERS (no pillar published yet) for "${pillar}": ${kids.join(', ')}`);
}
}
- Quarterly review. Any pillar with < 4 cluster links is weak; any orphan cluster (no pillar yet) needs a pillar built.
Implementation checklist
- Schema includes
pillar+isPillarfields. - Pillar pages auto-render the cluster list from frontmatter, not by hand.
- Each cluster has a back-link block.
- Audit script runs in prebuild and warns on weak pillars / orphans.
- Anchor text is descriptive on both sides.
After-launch verification
- Search Console → Performance → Pages: pillar URLs accumulate impressions for broad keywords.
- URL Inspection on a cluster shows the pillar in “Referring URLs”.
- Lighthouse → Accessibility → Links have discernible names — all green.
Common pitfalls
- Making the pillar a giant 10,000-word page that tries to be everything. Better: a structured overview that links out.
- Linking from pillar to clusters but not from clusters back. Two-way linking is what makes the cluster pattern work.
- Using the same anchor text for every link. Vary it naturally with related keywords.
- Building too many pillars. Five strong pillars beat fifteen half-built ones every time.
- Letting the cluster list go stale by hard-coding it in the pillar instead of generating it.
- Treating pillar/cluster as a URL structure (
/pillar/<x>/cluster/<y>/). It is a logical structure, not a URL one.
FAQ
- How long should a pillar page be?: 2000-4000 words is typical, but length matters less than coverage. If it links to 10 deep cluster articles, it doesn’t need to repeat them.
- Can a cluster page link to another cluster?: Yes, and it should when relevant. The pattern is a hub-and-spoke, but spokes can connect to each other too.
- Do I need a
/pillar/URL pattern?: No. The structure is logical, not URL-based. A pillar and its clusters can all live under/articles/. - What if I have overlapping pillars?: Pick one as canonical and merge the other, or scope them more narrowly so they don’t overlap.
- Should I add JSON-LD
WebPage aboutschema?: Optional but helpful. Mark pillars asWebPagewith anaboutproperty pointing to the topic.
Related
- Avoid content duplication when scaling fast
- Running a site-wide content audit
- Content site section structure
- Plan long-tail keyword site
- New site breadth or depth
Tags: #Indie dev #Content ops #SEO #Website planning #Pillar / Cluster #Technical SEO