> ## Documentation Index
> Fetch the complete documentation index at: https://docs.weaverse.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Rendering Weaverse Pages

> Complete guide to rendering different page types in Weaverse with loadPage() and WeaverseContent components.

# Rendering Weaverse Pages

This guide covers everything you need to know about rendering different page types in Weaverse, from basic concepts to advanced implementation patterns.

## Core Concepts

Weaverse page rendering revolves around two key elements:

1. **`loadPage()` function**: Loads page data based on type and handle
2. **`WeaverseContent` component**: Renders the loaded page content

### The loadPage() Function

The `loadPage()` function is called in your route loaders to fetch page data:

```typescript theme={null}
// In your route loader
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { handle } = params;
  const { weaverse } = context;
  
  const weaverseData = await weaverse.loadPage({
    type: "PRODUCT",     // Page type - determines content structure
    handle: handle,      // Page handle - identifies specific page
  });
  
  return { weaverseData };
}
```

### The WeaverseContent Component

The `WeaverseContent` component renders the loaded page data. It automatically gets the `weaverseData` from your loader through the `<WeaverseHydrogenRoot>` wrapper:

```tsx theme={null}
import { WeaverseContent } from "~/weaverse";

export default function ProductPage() {
  // No need to pass weaverseData explicitly - WeaverseHydrogenRoot handles it
  return <WeaverseContent />;
}
```

**How it works**: The `<WeaverseHydrogenRoot>` component (typically in your root layout) automatically extracts `weaverseData` from the current route's loader data and provides it to all child `<WeaverseContent>` components through React context. This eliminates the need to manually pass the data prop.

## Supported Page Types

Weaverse supports the following page types:

#### E-commerce Pages

* **`PRODUCT`** - Individual product pages
* **`COLLECTION`** - Collection/category listing pages

#### Content Pages

* **`PAGE`** - Custom pages created in Weaverse Studio
* **`BLOG`** - Blog listing pages
* **`ARTICLE`** - Individual blog post pages

#### Special Pages

* **`INDEX`** - Homepage
* **`CUSTOM`** - Custom pages created in Weaverse Studio (no specific handle needed)

## Working with Locales

Weaverse provides flexible localization support, allowing you to serve content in multiple languages and markets. Understanding how locale detection works is key to implementing internationalization correctly.

### Locale Format

Weaverse uses the standard `language-country` format for locales:

**Format:** `language-country` (lowercase, hyphen-separated)

* **Language:** ISO 639-1 code (2 letters, e.g., `sv`, `en`, `fr`)
* **Country:** ISO 3166-1 alpha-2 code (2 letters, e.g., `se`, `us`, `ca`)

**Examples:**

* `en-us` - English in United States
* `sv-se` - Swedish (`sv`) in Sweden (`se`)
* `fr-ca` - French (`fr`) in Canada (`ca`)
* `de-de` - German (`de`) in Germany (`de`)
* `ja-jp` - Japanese (`ja`) in Japan (`jp`)

<Note>
  **Important:** Always use lowercase with hyphen separator. Formats like `en_US`, `en-US`, or `EN-us` are not supported.
</Note>

### Locale Detection Methods

Weaverse provides three methods for detecting the current locale, with a clear priority order:

#### 1. Explicit locale Parameter (Recommended)

The most reliable method is to explicitly pass the `locale` parameter to `loadPage()`:

```typescript theme={null}
// app/routes/products.$productHandle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { productHandle } = params;
  const { weaverse } = context;

  // Explicitly specify the locale
  const weaverseData = await weaverse.loadPage({
    type: "PRODUCT",
    handle: productHandle,
    locale: "sv-se", // Load Swedish content
  });

  return { weaverseData };
}
```

**When to use explicit locale:**

* Custom routing logic determines the locale
* Loading content for a different locale than the current page
* Background jobs or server-side processes without request context
* Cross-locale content comparison or translation workflows

#### 2. Path Prefix Detection (Deprecated)

<Warning>
  This method is deprecated and may be removed in a future version. Use explicit `locale` parameter for new implementations.
</Warning>

Weaverse can extract locale from the URL path prefix:

```typescript theme={null}
// URL: /sv-se/products/jacket
// Automatically detects locale: "sv-se"

const weaverseData = await weaverse.loadPage({
  type: "PRODUCT",
  handle: productHandle,
  // locale auto-detected from path: /sv-se/...
});
```

#### 3. Auto-detection from context.storefront.i18n (Default)

When no explicit `locale` parameter is provided, Weaverse reads from Hydrogen's storefront context:

```typescript theme={null}
// Relies on Hydrogen's i18n configuration
const weaverseData = await weaverse.loadPage({
  type: "PRODUCT",
  handle: productHandle,
  // Automatically uses context.storefront.i18n.pathPrefix
});

// Behind the scenes:
// const pathPrefix = context.storefront.i18n.pathPrefix; // e.g., "/sv-se"
// const locale = pathPrefix.slice(1); // "sv-se"
```

### Basic Locale Usage

#### Auto-Detection Example

```typescript theme={null}
// app/routes/($locale).products.$productHandle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { productHandle } = params;
  const { weaverse, storefront } = context;

  // Weaverse automatically detects locale from context.storefront.i18n
  const [{ product }, weaverseData] = await Promise.all([
    storefront.query(PRODUCT_QUERY, {
      variables: { handle: productHandle }
    }),
    weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
      // locale auto-detected
    }),
  ]);

  return { product, weaverseData };
}
```

#### Explicit Locale Example

```typescript theme={null}
// app/routes/products.$productHandle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { productHandle } = params;
  const { weaverse } = context;

  // Determine locale from custom logic
  const locale = determineLocaleFromRequest(context.request);

  const weaverseData = await weaverse.loadPage({
    type: "PRODUCT",
    handle: productHandle,
    locale, // Explicitly pass locale
  });

  return { weaverseData, locale };
}

function determineLocaleFromRequest(request: Request): string {
  // Custom logic: subdomain, header, cookie, etc.
  const url = new URL(request.url);
  const subdomain = url.hostname.split(".")[0];

  const localeMap: Record<string, string> = {
    "se": "sv-se",
    "fr": "fr-fr",
    "de": "de-de",
  };

  return localeMap[subdomain] || "en-us";
}
```

### Advanced Locale Scenarios

#### Loading Multiple Locales

Load content in different locales simultaneously for comparison or translation workflows:

```typescript theme={null}
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { weaverse } = context;
  const { productHandle } = params;

  // Load same product in multiple locales
  const [englishData, frenchData, swedishData] = await Promise.all([
    weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
      locale: "en-us",
    }),
    weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
      locale: "fr-ca",
    }),
    weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
      locale: "sv-se",
    }),
  ]);

  return { englishData, frenchData, swedishData };
}
```

#### Dynamic Locale Selection

```typescript theme={null}
export async function loader({ params, context, request }: LoaderFunctionArgs) {
  const { weaverse } = context;
  let locale = "en-us"; // default

  // Priority 1: URL parameter
  if (params.locale && isValidLocale(params.locale)) {
    locale = params.locale.toLowerCase();
  }
  // Priority 2: User preference cookie
  else if (request.headers.get("cookie")?.includes("locale=")) {
    const cookieLocale = getCookieValue(request, "locale");
    if (cookieLocale && isValidLocale(cookieLocale)) {
      locale = cookieLocale;
    }
  }
  // Priority 3: Accept-Language header
  else {
    const acceptLanguage = request.headers.get("accept-language");
    locale = parseAcceptLanguageHeader(acceptLanguage) || "en-us";
  }

  const weaverseData = await weaverse.loadPage({
    type: "PRODUCT",
    handle: params.productHandle,
    locale,
  });

  return { weaverseData, locale };
}
```

### Locale Fallback Behavior

When a requested locale doesn't exist, Weaverse provides fallback content:

**Current Behavior:**

* If the specified locale doesn't exist, Weaverse falls back to `en-us` content
* This ensures users always see content, even if localization is incomplete

**Example:**

```typescript theme={null}
// Request Swedish content
const data = await weaverse.loadPage({
  type: "PRODUCT",
  handle: "jacket",
  locale: "sv-se",
});

// If sv-se doesn't exist:
// → Falls back to en-us content
// → Returns en-us page data
```

<Tip>
  **Coming Soon:** Smart fallback will try language matches before falling back to the default (e.g., try `sv-*` for `sv-se` request before falling back to `en-us`).
</Tip>

### Best Practices for Localization

<AccordionGroup>
  <Accordion title="Use Explicit locale Parameter" icon="hand-pointer">
    For maximum reliability and control, always explicitly pass the `locale` parameter:

    ```typescript theme={null}
    const weaverseData = await weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
      locale: determinedLocale, // Explicit is better
    });
    ```
  </Accordion>

  <Accordion title="Validate Locale Format" icon="spell-check">
    Ensure locales are properly formatted before passing to `loadPage()`:

    ```typescript theme={null}
    function normalizeLocale(input: string): string {
      return input.toLowerCase().trim().replace("_", "-");
    }

    const locale = normalizeLocale(params.locale || "en-us");
    ```
  </Accordion>

  <Accordion title="Handle Missing Locales Gracefully" icon="shield-halved">
    Check if the returned content matches the requested locale:

    ```typescript theme={null}
    const weaverseData = await weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
      locale: requestedLocale,
    });

    if (weaverseData?.page?.locale !== requestedLocale) {
      console.warn(`Locale ${requestedLocale} not found, using fallback`);
    }
    ```
  </Accordion>

  <Accordion title="Cache Per Locale" icon="database">
    Configure caching strategies to account for locale variations:

    ```typescript theme={null}
    const weaverseData = await weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
      locale,
      strategy: {
        maxAge: 3600, // Cache for 1 hour per locale
        staleWhileRevalidate: 86400,
      },
    });
    ```
  </Accordion>
</AccordionGroup>

### Related Localization Documentation

<CardGroup cols={2}>
  <Card title="Advanced Localization Guide" icon="globe" href="/guides/localization-advanced">
    Comprehensive technical guide covering locale detection, multi-project architecture, and advanced scenarios
  </Card>

  <Card title="Markets & Localization" icon="store" href="/features/markets-localization">
    Studio workflow for creating and managing localized content in Weaverse
  </Card>

  <Card title="WeaverseClient API" icon="code" href="/api-reference/weaverse-client">
    Complete API reference for loadPage() including all locale parameters
  </Card>

  <Card title="Custom Routing" icon="route" href="/features/custom-routing">
    Implementing custom URL structures with localization support
  </Card>
</CardGroup>

### Routes Without Weaverse Integration

Some routes in your Hydrogen theme may not use Weaverse's page system at all. These typically include functional pages that rely heavily on Shopify's built-in components and APIs:

* **Search pages** - Use Shopify's search API directly
* **Cart pages** - Use Shopify's cart components and forms
* **Account pages** - Use Shopify's customer account API
* **Policy pages** - Often render Shopify's policy content directly

### Custom Page Approach

Weaverse provides two ways to create custom pages:

#### Option 1: PAGE Type with Handle

For specific custom pages with defined handles:

```typescript theme={null}
// Example: About page with specific handle
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { weaverse } = context;
  
  const weaverseData = await weaverse.loadPage({
    type: "PAGE",
    handle: "about", // Specific page handle created in Studio
  });
  
  return data({ weaverseData });
}
```

#### Option 2: CUSTOM Type for Dynamic Routing

For catch-all routing where URL determines the page content:

```typescript theme={null}
// Example: Dynamic custom page routing
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { weaverse } = context;
  
  const weaverseData = await weaverse.loadPage({
    type: "CUSTOM", // No handle needed - URL determines content
  });
  
  return data({ weaverseData });
}
```

This approach allows you to create visually customizable pages using Weaverse's editor while maintaining functional pages that use Shopify's native components.

### Real-World Example: Pilot Template Pattern

The official Pilot template demonstrates this mixed approach. Here are examples from actual implementation:

```typescript theme={null}
// Homepage with dynamic routing (from Pilot template)
// app/routes/($locale)._index.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { pathPrefix } = context.storefront.i18n;
  const locale = pathPrefix.slice(1);
  let type: PageType = "INDEX";

  if (params.locale && params.locale.toLowerCase() !== locale) {
    // Update for Weaverse: if it's not locale, it's probably a custom page handle
    type = "CUSTOM";
  }

  const [weaverseData, { shop }] = await Promise.all([
    context.weaverse.loadPage({ type }),
    context.storefront.query<ShopQuery>(SHOP_QUERY),
  ]);

  return { shop, weaverseData };
}
```

```typescript theme={null}
// Catch-all route for custom pages (from Pilot template)
// app/routes/($locale).$.tsx
export async function loader({ context }: LoaderFunctionArgs) {
  const weaverseData = await context.weaverse.loadPage({
    type: "CUSTOM",
  });

  // Validate that the custom page exists
  validateWeaverseData(weaverseData);

  return { weaverseData };
}

export default function CustomPage() {
  return <WeaverseContent />;
}
```

This pattern shows how Pilot handles custom pages:

1. **Homepage route** (`_index.tsx`) detects when a URL parameter isn't a locale and switches to `"CUSTOM"` type
2. **Catch-all route** (`$.tsx`) handles any remaining unmatched routes as custom Weaverse pages
3. Both routes use `validateWeaverseData()` to ensure the page exists before rendering

```typescript theme={null}
// Search page without Weaverse (from Pilot template) 
// app/routes/($locale).search.tsx
export default function Search() {
  const { searchTerm, products } = useLoaderData<typeof loader>();
  
  // No WeaverseContent component - uses native Shopify search functionality
  return (
    <Section width="fixed" verticalPadding="medium">
      <Form method="get">
        <input name="q" placeholder="Search our store..." />
      </Form>
      <Pagination connection={products}>
        {/* Native Shopify components */}
      </Pagination>
    </Section>
  );
}
```

```typescript theme={null}
// Account page without Weaverse (from Pilot template)
// app/routes/($locale).account.tsx  
export default function Account() {
  const { customer } = useLoaderData<typeof loader>();
  
  // No WeaverseContent - uses Shopify's customer account components
  return (
    <Section>
      <AccountDetails customer={customer} />
      <AccountOrderHistory orders={orders} />
      <AccountAddressBook addresses={addresses} />
    </Section>
  );
}
```

## Basic Implementation

Here's the standard pattern for implementing Weaverse page rendering in a React Router v7 route:

### 1. Route Loader

```typescript theme={null}
// app/routes/products.$productHandle.tsx
import type { LoaderFunctionArgs } from "react-router";
import { data } from "@shopify/remix-oxygen";

export async function loader({ params, context }: LoaderFunctionArgs) {
  const { productHandle } = params;
  const { storefront, weaverse } = context;
  
  // Load both Shopify data and Weaverse page data in parallel
  const [productData, weaverseData] = await Promise.all([
    storefront.query(PRODUCT_QUERY, {
      variables: { handle: productHandle }
    }),
    weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
    }),
  ]);
  
  if (!productData.product) {
    throw new Response("Product not found", { status: 404 });
  }
  
  return data({
    product: productData.product,
    weaverseData,
  });
}
```

### 2. Route Component

```tsx theme={null}
import { useLoaderData } from "react-router";
import { WeaverseContent } from "~/weaverse";

export default function ProductPage() {
  // WeaverseHydrogenRoot automatically provides weaverseData to WeaverseContent
  return <WeaverseContent />;
}
```

## Page Type Examples

### Product Page

```typescript theme={null}
// app/routes/products.$productHandle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { productHandle } = params;
  const { storefront, weaverse } = context;
  
  const [{ product }, weaverseData] = await Promise.all([
    storefront.query(PRODUCT_QUERY, {
      variables: { handle: productHandle }
    }),
    weaverse.loadPage({
      type: "PRODUCT",
      handle: productHandle,
    }),
  ]);
  
  return data({ product, weaverseData });
}
```

### Collection Page

```typescript theme={null}
// app/routes/collections.$collectionHandle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { collectionHandle } = params;
  const { storefront, weaverse } = context;
  
  const [{ collection }, weaverseData] = await Promise.all([
    storefront.query(COLLECTION_QUERY, {
      variables: { handle: collectionHandle }
    }),
    weaverse.loadPage({
      type: "COLLECTION",
      handle: collectionHandle,
    }),
  ]);
  
  return data({ collection, weaverseData });
}
```

### Custom Page

```typescript theme={null}
// app/routes/pages.$pageHandle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { pageHandle } = params;
  const { storefront, weaverse } = context;
  
  const [{ page }, weaverseData] = await Promise.all([
    storefront.query(PAGE_QUERY, {
      variables: { handle: pageHandle }
    }),
    weaverse.loadPage({
      type: "PAGE",
      handle: pageHandle,
    }),
  ]);
  
  return data({ page, weaverseData });
}
```

### Blog Page

```typescript theme={null}
// app/routes/blogs.$blogHandle._index.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { blogHandle } = params;
  const { storefront, weaverse } = context;
  
  const [{ blog }, weaverseData] = await Promise.all([
    storefront.query(BLOG_QUERY, {
      variables: { handle: blogHandle }
    }),
    weaverse.loadPage({
      type: "BLOG",
      handle: blogHandle,
    }),
  ]);
  
  return data({ blog, weaverseData });
}
```

### Homepage

```typescript theme={null}
// app/routes/($locale)._index.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { storefront, weaverse } = context;
  const { pathPrefix } = context.storefront.i18n;
  const locale = pathPrefix.slice(1);
  
  let type: PageType = "INDEX";
  
  // Handle locale-based routing and custom pages
  if (params.locale && params.locale.toLowerCase() !== locale) {
    // If it's not a locale, it's probably a custom page handle
    type = "CUSTOM";
  }
  
  const [{ shop }, weaverseData] = await Promise.all([
    storefront.query(SHOP_QUERY),
    weaverse.loadPage({ type }),
  ]);
  
  return data({ shop, weaverseData });
}
```

## Custom Pages

Custom pages are created in Weaverse Studio and can be rendered using the `PAGE` type:

### Creating Custom Routes

For user-friendly URLs, create custom routes that map to Weaverse pages:

```typescript theme={null}
// app/routes/($locale).about.tsx - Maps /about to a Weaverse page
export async function loader({ context }: LoaderFunctionArgs) {
  const { weaverse } = context;
  
  const weaverseData = await weaverse.loadPage({
    type: "PAGE",
    handle: "about", // This should match the page handle in Weaverse Studio
  });
  
  return data({ weaverseData });
}

export default function AboutPage() {
  return <WeaverseContent />;
}
```

### Dynamic Custom Pages

Create a catch-all route for dynamic custom pages:

```typescript theme={null}
// app/routes/($locale).pages.$pageHandle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { pageHandle } = params;
  const { storefront, weaverse } = context;
  
  // Load both Shopify page data and Weaverse page data in parallel
  const [{ page }, weaverseData] = await Promise.all([
    storefront.query(PAGE_QUERY, {
      variables: { handle: pageHandle }
    }),
    weaverse.loadPage({
      type: "PAGE",
      handle: pageHandle,
    }),
  ]);
  
  // Handle page not found (neither Shopify nor Weaverse page exists)
  if (!page && !weaverseData) {
    throw new Response("Page not found", { status: 404 });
  }
  
  return data({ page, weaverseData });
}
```

## Error Handling

### 404 Error Handling

Always check if the page data exists and handle 404 cases:

```typescript theme={null}
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { handle } = params;
  const { weaverse } = context;
  
  const weaverseData = await weaverse.loadPage({
    type: "PRODUCT",
    handle,
  });
  
  // Check if Weaverse page exists
  if (!weaverseData) {
    throw new Response("Page not found", { status: 404 });
  }
  
  return data({ weaverseData });
}
```

### Fallback Rendering

Provide fallback content when Weaverse data is unavailable:

```tsx theme={null}
export default function ProductPage() {
  const { product, weaverseData } = useLoaderData<typeof loader>();
  
  return (
    <div>
      {weaverseData ? (
        <WeaverseContent />
      ) : (
        <div>
          {/* Fallback content when Weaverse page doesn't exist */}
          <h1>{product.title}</h1>
          <p>{product.description}</p>
        </div>
      )}
    </div>
  );
}
```

### Error Boundaries

Use error boundaries for graceful error handling:

```tsx theme={null}
import { ErrorBoundary } from "~/components/ErrorBoundary";

export default function ProductPage() {
  return (
    <ErrorBoundary>
      <WeaverseContent />
    </ErrorBoundary>
  );
}
```

## Performance Best Practices

### Parallel Data Loading

Always load Weaverse data in parallel with other API calls:

```typescript theme={null}
// ✅ Good: Parallel loading
const [shopifyData, weaverseData, reviewsData] = await Promise.all([
  storefront.query(PRODUCT_QUERY, { variables }),
  weaverse.loadPage({ type: "PRODUCT", handle }),
  getProductReviews(handle),
]);

// ❌ Bad: Sequential loading
const shopifyData = await storefront.query(PRODUCT_QUERY, { variables });
const weaverseData = await weaverse.loadPage({ type: "PRODUCT", handle });
const reviewsData = await getProductReviews(handle);
```

### Caching

Use proper cache headers for Weaverse pages:

```typescript theme={null}
import { routeHeaders } from "~/utils/cache";

export const headers = routeHeaders;

export async function loader({ context }: LoaderFunctionArgs) {
  // Your loader implementation
}
```

### Preloading

Consider preloading critical page data:

```typescript theme={null}
// Preload homepage data
export function preload({ context }: LoaderFunctionArgs) {
  return context.weaverse.loadPage({ type: "INDEX" });
}
```

## Troubleshooting

### Common Issues

#### 1. Page Type Mismatch

**Problem**: Wrong page type specified in `loadPage()`

```typescript theme={null}
// ❌ Wrong: Using PRODUCT type for a collection page
weaverse.loadPage({ type: "PRODUCT", handle: collectionHandle })
```

**Solution**: Use the correct supported page type

```typescript theme={null}
// ✅ Correct: Using COLLECTION type for a collection page
weaverse.loadPage({ type: "COLLECTION", handle: collectionHandle })
```

**Problem**: Using unsupported page types

```typescript theme={null}
// ❌ Wrong: These page types are not supported
weaverse.loadPage({ type: "SEARCH", handle: "search" })
weaverse.loadPage({ type: "CART", handle: "cart" })
weaverse.loadPage({ type: "ACCOUNT", handle: "account" })
```

**Solution**: Many functional pages don't need Weaverse integration - use native Shopify components instead

```typescript theme={null}
// ✅ Correct: Search, cart, and account pages often don't use Weaverse at all
// These routes use Shopify's native components directly:

// Search page - uses Shopify's search API and components
export default function SearchPage() {
  // Uses native Shopify search components, no WeaverseContent needed
}

// Cart page - uses Shopify's cart components and forms  
export default function CartPage() {
  // Uses native Shopify cart components, no WeaverseContent needed
}

// Account page - uses Shopify's customer account components
export default function AccountPage() {
  // Uses native Shopify account components, no WeaverseContent needed
}
```

**Alternative**: If you want to customize these pages visually, create custom Weaverse pages

```typescript theme={null}
// ✅ Alternative: Use PAGE type with custom page created in Studio (optional)
weaverse.loadPage({ type: "PAGE", handle: "custom-search-page" })
weaverse.loadPage({ type: "PAGE", handle: "custom-cart-page" })
weaverse.loadPage({ type: "PAGE", handle: "custom-account-page" })
```

#### 2. Missing Handle Parameter

**Problem**: Handle not provided or undefined

```typescript theme={null}
// ❌ Wrong: Handle is undefined
weaverse.loadPage({ type: "PRODUCT", handle: undefined })
```

**Solution**: Always validate handle exists

```typescript theme={null}
// ✅ Correct: Validate handle before using
invariant(handle, "Missing product handle");
weaverse.loadPage({ type: "PRODUCT", handle })
```

#### 3. Component Not Rendering

**Problem**: Weaverse components not registered properly

**Solution**: Ensure components are registered in `~/weaverse/components.ts` (or `~/app/weaverse/components.ts` in Pilot template):

```typescript theme={null}
import type { HydrogenComponent } from "@weaverse/hydrogen";
import * as ProductInfo from "~/sections/product-info";
import * as Hero from "~/sections/hero";

export const components: HydrogenComponent[] = [
  ProductInfo,
  Hero,
  // Add all your components here
];
```

**Important**: Always use namespace imports (`* as ComponentName`) and restart your dev server after registration.

#### 4. TypeScript Errors

**Problem**: Type errors with `WeaverseContent`

**Solution**: Ensure proper type imports:

```typescript theme={null}
import type { WeaverseLoaderData } from "@weaverse/hydrogen";

export async function loader({ context }: LoaderFunctionArgs) {
  const weaverseData: WeaverseLoaderData = await context.weaverse.loadPage({
    type: "PRODUCT",
    handle,
  });
  
  return data({ weaverseData });
}
```

### Debug Mode

Enable debug mode to troubleshoot page loading issues:

```typescript theme={null}
// In your weaverse.config.ts
export default defineConfig({
  debug: process.env.NODE_ENV === "development",
  // ... other config
});
```

## Next Steps

* Learn about [Custom Component Development](/development-guide/weaverse-component)
* Explore [Data Fetching Patterns](/development-guide/data-fetching)
* Master [Component Schemas](/development-guide/component-schema)
* Review [API Reference](/api-reference/introduction)

For more advanced use cases, see the [Migration Guide](/migration-advanced) and [Custom Routing](/features/custom-routing) documentation.
