Structuring Reusable Component Libraries in Hydrogen/Remix for Long-Term Storefront Scalability

When building large Hydrogen projects, component files can quickly become monolithic if data fetching, Shopify Storefront API bindings, and visual styling are tightly coupled inside single route files.

Here is a recommended folder structure and design pattern for keeping Hydrogen components modular, reusable, and easy to maintain across multiple pages:

Recommended Project Layout:

app/
├── components/
│   ├── ui/                # Presentational components (Button, Input, Badge)
│   ├── sections/          # Page sections (Hero, FeaturedCollection, ProductGrid)
│   └── global/            # Header, Footer, CartDrawer
├── fragments/             # Colocated GraphQL fragment queries
└── routes/                # Remix route handlers

Pattern: Decoupling Presentational UI from Data Loaders

To ensure a section (e.g., FeaturedCollection) can be reused anywhere (Homepage, PDP, custom landing pages), pass raw API node data as props rather than binding routes to hardcoded queries:

// app/components/sections/FeaturedCollection.tsx
import { ProductCard } from '~/components/ui/ProductCard';

export function FeaturedCollection({ title, products, layout = 'grid' }) {
  if (!products?.length) return null;

  return (
    <section className="py-12 px-4 max-w-7xl mx-auto">
      <h2 className="text-2xl font-bold mb-6">{title}</h2>
      <div className={layout === 'grid' ? 'grid grid-cols-2 md:grid-cols-4 gap-6' : 'flex overflow-x-auto gap-4'}>
        {products.map((product) => (
          <ProductCard key={product.id} product={product} />
        ))}
      </div>
    </section>
  );
}

This clean boundary allows developers to drop FeaturedCollection into any route loader outcome or CMS component renderer effortlessly.