Back to blog

Blocks vs Components in the ColoringStore migration

How ColoringStore keeps reusable UI components separate from route-specific product copy during the TanStack migration.

May 28, 2026ColoringStore TeamColoringStore Team

A framework migration gets messy when product copy, route wiring, and reusable UI primitives all live in the same file. ColoringStore keeps the split simple:

A block wires product content into a page. A component renders the props it receives.

Blocks are page wiring

Blocks live in src/blocks/. They read Paraglide messages, choose links, and compose shared components. They are intentionally easy to replace when the product page needs different navigation, a different call to action, or a different information hierarchy.

// src/blocks/header.tsx -- a block: reads i18n and chooses product links
export function Header() {
  const navLinks = [
    { href: '/text-to-coloring', label: m['coloring.nav.text_to_coloring']() },
    { href: '/gallery', label: m['coloring.nav.gallery']() },
  ];
  return <SiteHeader navLinks={navLinks} />;
}

Components are durable

Components live in src/components/. SiteHeader, SiteFooter, PricingTable, dialogs, tables, and shadcn/ui primitives should not know the product copy ahead of time. They survive the migration because all labels, links, and content arrive through props.

Why the split matters

ColoringStore can keep upstream SaaS machinery for auth, payments, credits, RBAC, admin, and storage while rewriting the product surface for coloring pages. The migration touches src/routes/, src/blocks/, src/components/coloring/, and messages/ without turning every reusable primitive into one-off project code.