Skip to main content

Getting Started

Welcome to Haus Tech's headless storefront ecosystem! This guide will help you get up and running with our components, libraries, and tools.

Overview

Haus Storefront Components is a collection of headless, cross-platform React components and hooks designed for building e-commerce storefronts. The library is organized into several categories:

  • Core Libraries: Foundation functionality including SDKs, providers, and data management
  • Common Libraries: Shared utilities, hooks, and UI components
  • Store Components: E-commerce specific components (cart, product list, checkout, etc.)
  • Plugin Configurations: Platform integrations and extensions

Prerequisites

Before you begin, make sure you have:

  • Node.js version 18 or higher
  • React 19 or higher
  • TypeScript (recommended for type safety)
  • Access to Haus Tech's private npm registry (contact the Haus Tech Team for access)
  • A Vendure backend API endpoint (Vendure is our default provider - see Vendure Integration for details)

Quick Start

1. Install the Core Library

The core library (@haus-storefront-react/core) is the foundation of the ecosystem and provides the DataProvider, SDKs, and essential hooks.

npm install @haus-storefront-react/core

Note: The package is not publicly available. Contact the Haus Tech Team for access to the private npm registry.

2. Set Up the DataProvider

Wrap your application with the DataProvider component. This provides the SDK, query client, and platform detection to all child components.

import { DataProvider } from "@haus-storefront-react/core";

function App() {
return (
<DataProvider
provider='vendure'
platform='web'
options={{
apiUrl: "https://your-vendure-api.com/shop-api",
vendureToken: "YOUR_CHANNEL_TOKEN",
persistOptions: {
enabled: false,
},
}}
>
<YourApplication />
</DataProvider>
);
}

3. Use Core Hooks

Once the DataProvider is set up, you can use hooks throughout your application:

import { useSdk, useQuery } from "@haus-storefront-react/core";

function ProductList() {
const sdk = useSdk();
const { data, isLoading, error } = useQuery({
queryKey: ["products"],
queryFn: () => sdk.search({ take: 10 }),
});

if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!data) return <div>No products found</div>;

return (
<div>
{data.items.map((product) => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>{product.description}</p>
</div>
))}
</div>
);
}

Basic Examples

Example 1: Shopping Cart

import { DataProvider } from "@haus-storefront-react/core";
import { Cart } from "@haus-storefront-react/cart";
import { AddItemToOrder } from "@haus-storefront-react/add-item-to-order";

function ShoppingApp() {
return (

<DataProvider
provider='vendure'
options={{
apiUrl: 'https://your-api.com/shop-api',
vendureToken: 'YOUR_TOKEN',
}}
>
<ProductPage />
<CartDisplay />
</DataProvider>
); }

function ProductPage() {
return (

<div>
<h1>Product Name</h1>
<AddItemToOrder.Root productVariantId='123'>
<AddItemToOrder.QuantityInput />
<AddItemToOrder.AddButton>Add to Cart</AddItemToOrder.AddButton>
</AddItemToOrder.Root>
</div>
); }

function CartDisplay() {
return (

<Cart.Root>
{({ data: order, isLoading }) => (
<>
{isLoading && <div>Loading cart...</div>}
{order && (
<>
<Cart.Items />
<Cart.Totals />
<Cart.CheckoutButton>Checkout</Cart.CheckoutButton>
</>
)}
<Cart.Empty>
<p>Your cart is empty</p>
</Cart.Empty>
</>
)}
</Cart.Root>
); }

import { DataProvider } from "@haus-storefront-react/core";
import { ProductList } from "@haus-storefront-react/product-list";
import { Search } from "@haus-storefront-react/search";

function Storefront() {
return (

<DataProvider
provider='vendure'
options={{
apiUrl: "https://your-api.com/shop-api",
vendureToken: "YOUR_TOKEN",
}} >
<Search.Root>
<Search.Input placeholder='Search products...' />
<Search.Results>
<Search.Products />
</Search.Results>
</Search.Root>

<ProductList.Root>
<ProductList.Items>
{({ items }) => (
<div>
{items.map((item) => (
<ProductList.Item key={item.id} product={item}>
<ProductList.Image />
<ProductList.Name />
<ProductList.Price />
<ProductList.AddToCartButton />
</ProductList.Item>
))}
</div>
)}
</ProductList.Items>
<ProductList.Pagination />
</ProductList.Root>
</DataProvider>

);
}

Example 3: Using Hooks Directly

import {
DataProvider,
useSdk,
useQuery,
useMutation,
} from "@haus-storefront-react/core";
import { useActiveOrder } from "@haus-storefront-react/hooks";

function CustomCart() {
const sdk = useSdk();
const { data: order, isLoading } = useActiveOrder();

const addItemMutation = useMutation({
mutationFn: (input) => sdk.addItemToOrder(input),
onSuccess: () => {
console.log("Item added to cart!");
},
});

const handleAddToCart = () => {
addItemMutation.mutate({
productVariantId: "123",
quantity: 1,
});
};

if (isLoading) return <div>Loading...</div>;

return (

<div>
<h2>Cart ({order?.lines.length || 0} items)</h2>
{order?.lines.map((line) => (
<div key={line.id}>
{line.productVariant.name} - {line.quantity} x {line.linePriceWithTax}
</div>
))}
<button onClick={handleAddToCart}>Add Item</button>
</div>
); }

Configuration Options

DataProvider Options

The DataProvider accepts various configuration options:

<DataProvider
provider='vendure'
platform='web' // or "native" - auto-detected if not specified
options={{
// Required
apiUrl: "https://your-api.com/shop-api",

// Optional
vendureToken: "YOUR_CHANNEL_TOKEN",
locale: "en",
currency: "USD",
autoSetLocaleFromDocument: true,

// Feature flags
enabledFeatures: {
customPriceCurrency: "SEK",
},

// State persistence
persistOptions: {
enabled: true,
maxAge: 3600000, // 1 hour in milliseconds
},

// Plugin configurations
pluginConfigs: [
// Your plugin configs here
],

// Custom interceptors
requestInterceptorStrategy: (config, defaultHandler) => {
// Custom request logic
return defaultHandler(config);
},
responseInterceptorStrategy: (response, defaultHandler) => {
// Custom response logic
return defaultHandler(response);
},

}}

>

<App />
</DataProvider>

Key Concepts

Headless Components

All components are "headless" - they provide logic and state management but allow complete UI customization. You control the styling and layout.

Compound Components

Many components use the compound component pattern for flexibility:

<Cart.Root>
<Cart.Items>
<Cart.Item orderLine={line}>
<Cart.Item.Image />
<Cart.Item.Price />
</Cart.Item>
</Cart.Items>
<Cart.Summary>{/* Summary content */}</Cart.Summary>
</Cart.Root>

asChild – Use Your Own Elements in Compound Components

Many components in the Haus Storefront libraries have an asChild prop. This allows you to replace the underlying HTML element (such as a <button>, <input>, or <form>) with your own component or element, without losing access to all the logic, props, and context.

This is particularly useful if you want to use your own styling, a UI library, or for example, a Next.js Link instead of a regular <a> tag.

Example: Replace Button with Your Own Component

import { Login } from "@haus-storefront-react/authentication";

<Login.Root>
<Login.Form asChild>
<form className='my-form'>
<Login.EmailInput asChild>
<input className='my-input' />
</Login.EmailInput>
<Login.PasswordInput asChild>
<input className='my-input' />
</Login.PasswordInput>
<Login.SubmitButton asChild>
<button className='my-button'>Log in</button>
</Login.SubmitButton>
</form>
</Login.Form>
</Login.Root>;

In the example above:

  • All props and event handling from the Login components are passed through to your own elements.
  • You can use any element (e.g., <button> or a custom React component) and still get the correct logic and accessibility.

Why Use asChild?

  • Full control over markup and styling – You decide exactly which element is rendered.
  • Keep all logic – You still get the correct props, events, and accessibility attributes.
  • Integrate with UI libraries – Works with Chakra UI, Material UI, Tailwind, or any other UI library.

asChild is built on a "slot" pattern and works in all compound components where it's available.

Render Props

Components often use render props to provide context:

<ProductList.Root>
{({ products, isLoading, error }) => {
// Access context data
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading products</div>;

return <div>{/* Your UI */}</div>;

}}

</ProductList.Root>

Platform Detection

Components work on both web (React) and mobile (React Native) platforms. The library automatically detects the platform and uses appropriate primitives. You can explicitly set the platform if needed:

  • Web: Runs in browsers, uses localStorage for persistence
  • Native: Runs in React Native, uses AsyncStorage for persistence
<DataProvider
provider='vendure'
platform='web' // Explicitly set platform
options={{ apiUrl: "..." }}
>
<App />
</DataProvider>

Next Steps

Now that you have the basics set up, explore the documentation:

  1. Learn about the Core Library: Understand the DataProvider, SDKs, and hooks
  2. Explore Storefront Components: Build your storefront with our pre-built components
  3. Use Storefront Hooks: Access e-commerce functionality with React hooks
  4. Customize Components: Use the ComponentProvider to customize component rendering
  5. Integrate with Vendure: Learn about plugin configurations and type-safe integrations
  6. Level up with Advanced Guides: Dive into Advanced Playbook for component composition, SDK extensions, and plugin strategies

Happy building!

Made with ❤️ by Haus Tech Team