Pillar/cluster is not an SEO trick. It is a way of organizing content that mirrors how Google’s helpful-content systems and AI answer engines both judge whether a site actually knows a topic. When done right, the pillar page ranks for the broad term, each cluster page ranks for a long-tail variation, and the link graph between them tells crawlers “this site has depth here.” The mistake most indie devs make is keeping that structure in their head instead of encoding it in their content schema, where a build script can enforce it.
TL;DR
Treat pillar/cluster as data: add a pillar field to your frontmatter schema, generate the “in this guide” list and the “back to pillar” link automatically, write descriptive anchor text in both directions, and run an audit script that flags weak pillars (fewer than 4 clusters) and orphan clusters (no pillar published yet). Five strong pillars beat fifteen half-built ones. Code for all of it is below.
What pillar and cluster pages actually are
A pillar page covers a topic broadly (e.g. “Setting up a content site”). A cluster page covers one subtopic in depth (e.g. “Submitting your sitemap to Search Console”). Each cluster links up to the pillar; the pillar links down to each cluster. Google reads that link graph and concludes the site has invested seriously in the subject. The same bidirectional structure is what AI answer engines look for: as of June 2026, ChatGPT, Perplexity, Gemini, and Google AI Mode increasingly cite topically-coherent content ecosystems over isolated one-off posts, because interconnected coverage reads as genuine expertise rather than a single lucky article.
Recommended length, based on what currently ranks and gets cited:
| Page type | Word count (typical) | Job |
|---|---|---|
| Pillar | 2,500-4,000 | High-level overview; links to every cluster; canonical authority for the topic |
| Cluster | 1,200-2,500 | Answer one specific question or cover one aspect, deeply |
Length is a guideline, not a target. A pillar that links to 10 deep clusters does not need to repeat them; coverage across the cluster set matters more than the pillar’s own word count.
How to tell you already have the structure (or don’t)
- 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 3 clicks from the homepage. (Pages buried 4+ clicks deep get crawled less and are treated as less important.)
- Internal anchor text uses the actual target keyword, not “click here” or “read more” — generic anchors pass almost no topical signal.
- A new article fits into an existing pillar without manual reshuffling.
If two or more of those are false, the steps below close the gap.
Before you start
- You have at least 20-30 published articles to organize. Below that, build clusters first and add the pillar later.
- Your content schema can take a new
pillarfield. - You can run a small Node script in prebuild to audit links.
Step by step
- Add a
pillarfield to frontmatter. Extend the Zod schema so the relationship is data, not memory:
// 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. More than 10 pillars and you are spread too thin — Google rewards depth on a few topics over thin coverage of many.
-
Write cluster articles first. A pillar drafted before its clusters comes out abstract. Ship 5-7 clusters per pillar, then write the pillar. The pillar will be richer and more honest because you will have already done the hard thinking.
-
Generate the pillar’s “child” list automatically. In the pillar’s layout, never hand-maintain the list:
---
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 the pillar automatically. In the cluster’s layout, render a “part of” block so the up-link is never forgotten:
---
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-to-cluster anchors describe the cluster’s topic; cluster-to-pillar anchors describe the pillar’s topic. Vary the wording so you are not repeating one exact-match phrase on every link. A workable 2026 mix per destination page: roughly 15-25% exact-match keyword, 30-40% partial-match, the rest semantic variants. Never use “click here,” “read more,” or the literal slug.
-
Run a pillar/cluster audit script. Find weak pillars and orphan clusters before Google does:
// 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(', ')}`);
}
}
- Review quarterly. Any pillar with fewer than 4 cluster links is weak. Any orphan cluster (no pillar yet) needs a pillar built. Wire the script into prebuild so a weak pillar shows up as a build warning, not a surprise three months later.
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 and orphans.
- Anchor text is descriptive and varied on both sides.
How to verify it worked
The authority signal accumulates over months, not days, so check leading indicators first:
- Search Console → Performance → Pages: pillar URLs accumulate impressions for broad keywords; clusters pick up long-tail queries.
- URL Inspection on a cluster: the pillar appears under “Referring page” / referring URLs, confirming the up-link is crawled.
- Lighthouse → Accessibility → “Links have discernible names”: all green means your anchor text is real text, not bare icons or empty links.
- Crawl depth: spot-check that every cluster is reachable within 3 clicks of the homepage.
Common pitfalls
- One giant 10,000-word pillar that tries to be everything. A structured overview that links out beats a wall of text that buries each subtopic.
- Linking pillar-to-cluster but not back. The bidirectional link is the entire mechanism; a one-way link leaves the cluster looking orphaned.
- The same anchor text on every link. Vary it with related keywords; identical exact-match anchors look manipulative and add no new signal.
- Too many pillars. Five strong pillars beat fifteen half-built ones every time.
- Hard-coding the cluster list in the pillar. It goes stale the moment you publish the next cluster. Generate it.
- Treating pillar/cluster as a URL structure (
/pillar/<x>/cluster/<y>/). It is a logical structure, not a URL one. The pages can all live flat under/articles/.
FAQ
- How long should a pillar page be? As of June 2026, 2,500-4,000 words is typical, but coverage matters more than length. A pillar that links to 10 deep clusters does not need to repeat them.
- Can a cluster page link to another cluster? Yes, and it should when relevant. The pattern is hub-and-spoke, but spokes can connect to each other when the topics genuinely relate.
- 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 stop overlapping. Two pillars competing for the same broad term split your authority.
- Does this help with AI search, not just Google? Yes. Answer engines (ChatGPT, Perplexity, Gemini, Google AI Mode) preferentially cite interconnected, topically-coherent sets of pages with clear authorship over isolated posts, so the same structure helps you get cited.
- Should I add JSON-LD
WebPage aboutschema? Optional but helpful. Mark pillars asWebPagewith anaboutproperty pointing to the topic entity so the topical relationship is explicit in structured data.
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
External references: Google Search Central — internal links best practices and Search Engine Land’s guide to topic clusters.
Tags: #Indie dev #Content ops #SEO #Website planning #Pillar / Cluster #Technical SEO