Bulk Site Scaffolding with Shared Components — Scaling from 2 to 14 Sites
Bulk Site Scaffolding with Shared Components
Problem
Started with 2 sites (zendee-adventures, squamish-ai) and needed to import 12 more businesses from diverse sources: GitHub repos, Lovable apps (lovable.app), Instagram profiles, personal websites, and LinkedIn. Each site needs its own brand config, CLAUDE.md context, visual identity, and full Astro page structure — but they should share navigation patterns and link to each other.
Investigation & Approach
1. Discovery: Identifying businesses across platforms
- Used
gh repo list veganpolice --limit 50 --json name,descriptionto scan GitHub repos - Used
WebFetchto scrape Lovable app URLs for business details - Attempted Instagram and LinkedIn scraping (both login-gated, got partial data)
- Presented a curated list for the user to confirm/reject (filtered out personal projects, forks, tools)
Key insight: Not all repos are businesses. Present candidates and let the user decide. Some are “projects to track” not active businesses — the system should accommodate both.
2. Bulk scaffolding: 11 agents in parallel
Rather than running /site-scaffold 12 times sequentially (which asks for brand info each time), we:
- Read the template files from
apps/zendee-adventures/once - Defined brand config for each business based on available context
- Launched 11 parallel agents, each scaffolding one complete site
- All 11 completed in ~80 seconds total (vs ~15 min sequential)
Template files per site (13 files):
package.json,astro.config.mjs,tsconfig.jsonbrand.config.ts,CLAUDE.mdpublic/_headerssrc/styles/brand.css,src/styles/global.csssrc/layouts/SiteLayout.astrosrc/pages/index.astro,src/pages/blog.astro,src/pages/blog/[...slug].astrosrc/content.config.ts
3. Shared component extraction
After scaffolding, all 14 sites had identical header/footer markup (~590 lines duplicated). Extracted to @repo/ui:
SiteHeader.astro— takesname+navLinks[]propsSiteFooter.astro— takesnamepropBusinessDirectory.astro— renders grid of all businesses, excludes current site
Central business registry: packages/ui/src/data/businesses.ts — single source of truth for all business names, slugs, domains, taglines, and pages URLs.
4. CI/CD for 14 sites
The existing deploy workflow already looped over apps/*/dist, so it scaled automatically. Added:
- Auto-create Cloudflare Pages projects on first deploy (no manual wrangler setup)
workflow_dispatchsupport for deploying specific sites or all
Key Design Decisions
1. Parallel agents for bulk scaffolding
Why: 11 sequential scaffolds would take 15+ minutes and block the conversation. Parallel agents complete in ~80 seconds with identical quality.
Pattern: Read template once → define per-site config → launch N parallel agents → verify all build.
2. Props-based shared components (not config-importing)
Why: SiteHeader takes name and navLinks as props rather than importing brand.config.ts internally. This keeps components pure — no cross-package config dependencies. Each site controls its own nav links.
<!-- Good: explicit props -->
<SiteHeader name={config.name} navLinks={[
{ label: 'Blog', href: '/blog' },
{ label: 'Contact', href: '#contact' }
]} />
<!-- Bad: component imports config internally -->
<!-- Creates hidden coupling, harder to customize per-site -->
3. Central business registry separate from brand configs
Why: BusinessDirectory.astro needs to know about all businesses, but importing 14 brand.config.ts files would create circular deps. Instead, packages/ui/src/data/businesses.ts is a lightweight registry with just the display data.
Trade-off: Data is duplicated between brand.config.ts and businesses.ts. Acceptable because the registry is the source of truth for cross-site linking, while brand config is the source of truth for per-site behavior.
4. Auto-create Cloudflare Pages projects in CI
Why: Running wrangler pages project create locally requires auth. Adding auto-create to CI means new sites deploy automatically on first push with zero manual Cloudflare setup.
# Auto-create if project doesn't exist
if ! npx wrangler pages project list | grep -q "^$site_name$"; then
npx wrangler pages project create "$site_name" --production-branch=main || true
fi
Prevention / Best Practices
-
When adding a new site: Use
/site-scaffold <slug>for single sites, or batch with parallel agents for multiple. Always add to the businesses table in CLAUDE.md ANDpackages/ui/src/data/businesses.ts. -
When shared markup appears in 3+ sites: Extract to
@repo/ui. Keep components props-based, not config-importing. -
When importing from external sources: Use
/content-sync <slug>to pull real content from GitHub repos, Lovable apps, etc. into the scaffolded sites. -
Nav links vary per site — this is by design. Each site’s index.astro controls its own navLinks array. Common patterns: Blog + Contact, Blog + Get Involved, Blog + Follow.
Files Created/Modified
New shared components (packages/ui):
src/components/SiteHeader.astrosrc/components/SiteFooter.astrosrc/components/BusinessDirectory.astrosrc/data/businesses.ts
New sites (12):
apps/aaronr/,apps/adventure-oasis/,apps/adventure-weddings/apps/audacious-art/,apps/create-makerspace/,apps/function-growth/apps/healthcal/,apps/hot-tub-every-day/,apps/mountain-life/apps/pirate-radi0/,apps/sendy-adventures/,apps/southside-lodge/
New skill:
.claude/skills/content-sync/SKILL.md
Modified:
- All 14 sites’ pages (42 files) — replaced inline header/footer with shared components
.github/workflows/deploy.yml— auto-create projects, workflow_dispatch supportCLAUDE.md— businesses table updated to 14 entries
Cross-References
- See also:
spa-scraping-and-second-wave-scaffolding.md(Phase 2: 14→24 sites)