A blog hub that builds its own topic pages
AHA’s blog was a hand-written list of 11 posts with almost no links to the pages that sell. Now one post list and one taxonomy build the hub, topic pages and schema.
The problem
A hand-written list of 11 posts, with no topics and almost no links to sales pages.
The solution
One post list and one taxonomy generate the hub, topic pages and schema.
Most of AHA’s search traffic came from people who already knew the company. From 25 May to 24 August, 89.7% of the clicks Google could tie to a search term came from someone typing AHA’s name. A blog is how you reach everyone else, but only if search engines can tell what it covers.
Why a list wasn’t enough
At the start of September the blog was a hand-written list of 11 cards. That had three problems:
- No topics. A search engine saw eleven unrelated URLs, not a company that covers smart locks or lighting properly.
- Almost no links out. The August audit found exactly one in-body link across the whole blog, so the posts passed almost nothing to the pages that sell.
- Schema that drifted. The hub’s ItemList was typed by hand, had fallen out of sync with the real posts, and had to leave out noindex posts by hand.
One list, one taxonomy
On 4 September I rebuilt the hub as a data-driven system, with Claude Code writing much of the code:
posts.jsis the single source of truth. One entry wires a post into the hub, its topic page and the “Keep reading” block on other posts.blogTaxonomy.jsdefines the topics. Each topic feeds one commercial page, and every post in it links to that page in its body, with descriptive anchor text.- The ItemList is computed from
posts.js, so it is correct by construction, and noindex posts can’t leak into it. - A topic gets its own page at three posts. One rule, in one data file:
// blogTopicHubs.js, simplified
module.exports = taxonomy.categories
.map(cat => ({ ...cat, posts: posts.filter(p => p.category === cat.slug) }))
.filter(cat => cat.posts.length >= taxonomy.minPostsForHub); // 3
// blog-topic.njk paginates over this list: one /blog/topics/<slug> page each
Why three? A topic page with one post on it is a thin page that competes with the post it links to. Below the threshold, a topic is still a filter chip on the hub; it just doesn’t get a URL until it earns one. The third post creates the page, and the sitemap picks it up on the next build.
On 10 September a second level went on top: four categories, each holding two topics. No existing URL changed. Before, 11 of the 19 posts sat in topics too small for a page; after, every post was on a live category page. The build fails if a topic is in no category or in two, so a bad regroup can’t ship quietly.
That pass also exposed a live bug. Topic pages had never carried their BreadcrumbList or ItemList schema, because both were set with Nunjucks set tags in the template body, which the layout never sees. They are now computed in data files too.
Making each post earn its click
Structure only helps if the posts deserve a click. In September:
- Titles that match the query. The cove lighting guide sat at position 9 for “what is cove in false ceiling” and got no clicks. It became “What Is Cove Lighting in a False Ceiling? A Mumbai Guide”.
- Answer first, with sources. 12 posts were rewritten to answer in their first two sentences, with question-shaped headings, at least two fetched sources, an honest-limits section and a visible “updated” date.
- Schema that matches the page. FAQ schema was synced to the visible text on 10 posts. One had six schema questions that weren’t on the page at all.
- Unique covers. An audit of 29 posts found two cover images shared between posts. Every card, social and schema image is now unique.
- Real authors. Author pages carry Person schema, and every byline links to one, so a name resolves to a person rather than a string.
Results
The Journal grew from 11 posts to 48. I wrote 43 of them, and they’re all listed here.
In the 28 days from 31 August to 27 September, against the 28 days before, blog clicks rose twelvefold and non-brand clicks rose 77%. By 12 October, two of the three targets set for 3 December were beaten, seven weeks early. The third, non-brand clicks on named queries, was still open.
The honest caveat: the growth is informational. By 22 October, commercial searches (people shopping for home automation) hadn’t moved.
Rules I’d reuse
- Derive, don’t maintain. Cards, topic pages, schema and sitemap entries all come from one list.
- Make pages earn their URL. No topic page below three posts.
- Generate schema from visible text, so the two can’t disagree.
- Let the build reject bad structure, instead of hoping a reviewer spots it.