🍏 iOS / Mac OSSoftRAID 9 adds RAID 6 for macOS 27 owners only(17.09.2026 um 20:07 Uhr)
🍏 iOS / Mac OSSoftRAID 9 adds RAID 6 for macOS 27 owners only(17.09.2026 um 20:07 Uhr)
🔧 Programmierung 🕛 vor 2 Monaten 5 Min Lesezeit
0

Building in public, week 17: turning one feature into a page cluster (and the internal-linking layer nobody sees)

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Week 16 shipped the AI background remover: Rust-native, ort + ISNet + libvips, no Python. That was the feature. Week 17 was not about writing more of it. It was about the boring, high-leverage part that most side projects skip: turning one working feature into pages that can actually rank, and wiring those pages together so search engines can find them.



No new engine code this week. Just leverage on what already existed. Here is what that actually looked like.






The problem: a hub with nothing pointing at it



The background remover lives at /remove-background. That is the hub. The plan was classic hub-and-spoke: one general tool page, then use-case spokes that each target a specific intent (removing a signature background, prepping an Amazon product photo, and so on).



I built two spokes this week. But halfway through, I looked at how internal links actually worked on the site and found the real problem: nothing linked from the hub to the spokes. The spokes linked back to the hub in their body text, but the hub had no idea they existed. Neither did the ~180 converter pages.



Tool links on the site were hardcoded in a frontend constant, roughly:




CODE
export const IMAGE_TOOLS = [
{ label: "Compress JPG", href: "/compress/jpg", tool: "compress" },
{ label: "Resize Image", href: "/resize-image", tool: "resize" },
{ label: "Crop Image", href: "/crop-image", tool: "crop" },
{ label: "Images to PDF", href: "/images-to-pdf", tool: "convert" },
] as const;






That list covered the converter tools. It did not include the background remover or its spokes at all. So the new pages were orphans: reachable only through the sitemap, with no internal links carrying any signal to them. For a domain that is still young and still earning Google's trust, orphan pages get discovered slowly and rank even slower.






The fix: one constant as the source of truth



Instead of hardcoding links in three different places, I made a single constant describe the whole cluster:




CODE
export const BACKGROUND_TOOLS = [
{ label: "Background Remover", href: "/background-remover" },
{ label: "Signature Background", href: "/remove-signature-background" },
{ label: "Amazon Product Photo", href: "/amazon-product-photo-background" },
] as const;






Then a small server component renders it, with an exclude prop so each page drops itself from the list:




CODE
export default function BackgroundTools({ exclude }: { exclude?: string }) {
const tools = BACKGROUND_TOOLS.filter((t) => t.href !== exclude);
if (tools.length === 0) return null;

return (
<section>
<h2>Background Removal</h2>
<div>
{tools.map(({ label, href }) => (
<Link key={href} href={href}>{label}</Link>
))}
</div>
</section>
);
}






The nice part is that the same constant became the source of truth for detecting a background-removal page too. The main layout used to repeat the same array-of-slugs check three separate times. That collapsed into one line:




CODE
const isBgRemoval = BACKGROUND_TOOLS.some((tool) => tool.href === `/${mode}`);






So now: the hub links to both spokes, each spoke links to the hub and its sibling, and the whole thing renders only on background-removal pages. Adding a fourth spoke later is one line in the constant and it is cross-linked everywhere automatically. I also added the same list as a footer column, which puts the links on every page of the site, not just the cluster.






The bug that only shows up in production



The two spokes are stored as structured content in the database, and each section body is rendered with dangerouslySetInnerHTML inside a <p>. On one spoke I added a small "before you upload" checklist as a <ul>. It looked fine locally. In production, React threw a hydration mismatch.



The reason is plain once you see it: a <ul> is not valid inside a <p>. The browser silently auto-closes the paragraph before the list, so the DOM the browser builds does not match the HTML React rendered on the server, and hydration fails. The fix was to keep the section body to inline content only, so the checklist became <br>-separated lines instead of a real list.



The lesson I actually kept: I added a build-time guard that scans every section body for block-level tags (ul, ol, li, div, table, headings) and fails the build if it finds one. The class of bug is now impossible to ship again, which matters more than the one fix.






The honest numbers



Building in public means posting the numbers even when they are boring or bad.





  • Background pages live: 3 (hub + 2 spokes). The plan asked for 1 or 2, so this part went well.


  • Benchmark on the real VPS: the background remover runs at about p50 2.4s, p95 9.6s per image on a 4-core box.


  • AI citations: the site is cited 75 times across Microsoft Copilot, mostly on HEIC content. That is visibility that never shows up in a rank tracker, and I only found it by looking.


  • Google: still sandboxed. Impressions exist, positions are deep (average around the mid-40s), clicks are a trickle. Bing, meanwhile, ranks the same pages on page one. Same content, different trust curve. This is normal for a young domain and I am not fighting it, just waiting it out while the content compounds.






What week 18 looks like



More spokes, but slowly. Google's spam updates punish thin template-swap pages, so the rule is one or two good pages with genuine unique data, not ten shells. On the list: a couple more use-case spokes, a low-competition webp remove background page I found in my keyword data, and continuing the slow grind of backlinks to lift domain authority, which is the real ceiling right now.



The feature was week 16. The leverage was week 17. If you are building something similar, the thing I would repeat is this: shipping the feature is the easy half. Making it findable, and keeping the pages honestly connected, is the half that actually decides whether anyone ever sees it.



You can try the background remover here: https://convertifyapp.net/remove-background

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
OpenAI details more cases of AI agents taking unauthorized actions
1 Quelle
The answer to AI uncertainty is adaptability, not paralysis
1 Quelle
FritzOS: 6 FritzBoxen erhalten Beta-Updates - inkl. neuer Funktion
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building in public, week 17: turning one feature into a page cluster (and the internal-linking layer nobody sees)

Thematisch verwandte Begriffe: Building, public, week, turning · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...