Skip to main content

Components

Tune Me In uses Shopify Hydrogen’s component architecture, which distinguishes between server and client components for optimal performance.

Server vs Client Components

Hydrogen uses file naming conventions to determine where components run:
  • .server.jsx - Components that run only on the server (can fetch data, access secrets)
  • .client.jsx - Components that run on the client (can use React hooks, state, interactivity)

Server Components

Server components render on the server and have access to server-side data. They cannot use client-side features like useState or useEffect.
src/components/Layout.server.jsx
import {LocalizationProvider} from '@shopify/hydrogen';
import HeaderScroll from './simplistic/HeaderScroll.client';
import Footer from './Footer.server';
import Cart from './Cart.client';

export default function Layout({children}) {
  return (
    <LocalizationProvider>
      <HeaderScroll />
      <div className="min-h-screen max-w-screen text-gray-700">
        <Cart />
        <main id="mainContent">{children}</main>
        <Footer />
      </div>
    </LocalizationProvider>
  );
}

Client Components

Client components run in the browser and can use React hooks and handle interactivity.
src/components/Button.client.jsx
export default function Button(props) {
  const {children} = props;
  return (
    <div className="block w-full text-white text-sm bg-black px-3 py-4 disabled:cursor-wait disabled:opacity-60">
      {children}
    </div>
  );
}

Component Structure

Tune Me In organizes components into several directories:
src/components/
├── annotations/       # Portable Text annotation components
├── blocks/           # Portable Text block components
├── icons/            # SVG icon components
├── simplistic/       # Theme-specific components
├── Button.client.jsx
├── Cart.client.jsx
├── Header.server.jsx
├── Layout.server.jsx
└── ...

Creating a New Component

1

Choose the component type

Determine if your component needs client-side interactivity or can be server-rendered.Use .client.jsx if you need:
  • React hooks (useState, useEffect, etc.)
  • Browser APIs
  • Event handlers
Use .server.jsx if:
  • No interactivity needed
  • Fetching data from Sanity or Shopify
  • Better performance is desired
2

Create the component file

Create a new file in src/components/ with the appropriate extension:
src/components/ProductBadge.client.jsx
export default function ProductBadge({text, variant = 'default'}) {
  const styles = {
    default: 'bg-gray-200 text-gray-800',
    sale: 'bg-red-500 text-white',
    new: 'bg-blue-500 text-white',
  };
  
  return (
    <span className={`px-2 py-1 text-xs font-medium rounded ${styles[variant]}`}>
      {text}
    </span>
  );
}
3

Import and use the component

Import your component in a page or another component:
import ProductBadge from '../components/ProductBadge.client';

export default function ProductCard({product}) {
  return (
    <div>
      <ProductBadge text="New" variant="new" />
      {/* ... */}
    </div>
  );
}

Using Hydrogen Components

Hydrogen provides built-in components for common e-commerce patterns:

Product Components

import {Product} from '@shopify/hydrogen/client';

export default function ProductCard({product}) {
  return (
    <Product product={product.storefront}>
      <div className="bg-white">
        <Product.SelectedVariant.Image
          className="w-full object-cover"
          options={{width: 800}}
        />
        <Product.Title />
        <Product.SelectedVariant.Price className="text-gray-900" />
      </div>
    </Product>
  );
}

Add to Cart

src/components/ButtonSelectedVariantAddToCart.client.jsx
import {Product} from '@shopify/hydrogen/client';
import Button from './Button.client';

export default function ButtonSelectedVariantAddToCart() {
  return (
    <Product.SelectedVariant.AddToCartButton>
      <Button>Add to Cart</Button>
    </Product.SelectedVariant.AddToCartButton>
  );
}

Component Best Practices

Use server components by default for better performance. Only use client components when you need interactivity or browser APIs.
Each component should have a single responsibility. Break down complex components into smaller, reusable pieces.
While this starter uses JSX, consider migrating to TSX for better type safety:
interface ButtonProps {
  children: React.ReactNode;
  variant?: 'primary' | 'secondary';
  onClick?: () => void;
}

export default function Button({children, variant = 'primary', onClick}: ButtonProps) {
  // ...
}

Next Steps

Pages

Learn how to create and structure pages in Hydrogen

Styling

Style your components with Tailwind CSS