# Metafield Frontend Rendering Guide

## Overview

This guide explains how to display metafields on the frontend using our Shopify Liquid-like template system.

---

## Quick Start

### 1. Using the Puck Component (Recommended)

The easiest way to display metafields is through the **Metafield Display** component in Puck customizer:

1. Open the Puck customizer
2. Add a **"Metafield Display"** component to your page
3. Configure:
   - **Entity Type**: product, collection, or blog
   - **Entity ID**: Leave empty to use current page entity
   - **Namespace**: e.g., "custom"
   - **Metafield Key**: e.g., "fabric_type"
   - **Prefix/Suffix**: Optional text before/after value
   - **CSS Class**: Style the output

**Example Configuration:**
```
Entity Type: product
Namespace: custom
Key: fabric_type
Prefix: "Material: "
CSS Class: text-lg font-semibold text-gray-800
```

**Output:** `Material: 100% Cotton`

---

### 2. Using in Custom React Components

```javascript
import { useMetafields } from '@/utils/metafieldRenderer';

function ProductDetails({ productId }) {
  const { metafields, loading, getField, renderField } = useMetafields('product', productId);

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

  return (
    <div>
      {/* Simple value access */}
      <p>Fabric: {getField('custom', 'fabric_type')}</p>

      {/* Formatted rendering */}
      <p>{renderField('custom', 'fabric_type', {
        format: {},
        fallback: 'Not specified'
      })}</p>

      {/* Display all metafields from a namespace */}
      <div>
        <h3>Specifications</h3>
        {metafields
          .filter(mf => mf.namespace === 'specifications')
          .map(mf => (
            <div key={mf.key}>
              <strong>{mf.label}:</strong> {mf.value}
            </div>
          ))
        }
      </div>
    </div>
  );
}
```

---

### 3. Using Utility Functions (Non-React)

```javascript
import { getMetafield, renderMetafield } from '@/utils/metafieldRenderer';

// Get raw value
const fabricType = await getMetafield('product', productId, 'custom', 'fabric_type');
console.log(fabricType); // "100% Cotton"

// Render with template
const html = await renderMetafield('product', productId, 'custom', 'fabric_type', {
  prefix: 'Material: ',
  wrapper: '<div class="metafield-value">{{value}}</div>'
});
console.log(html); // <div class="metafield-value">Material: 100% Cotton</div>
```

---

## Template Options

### Basic Options

```javascript
{
  prefix: 'Material: ',        // Text before value
  suffix: ' (imported)',       // Text after value
  wrapper: '<span>{{value}}</span>',  // HTML wrapper
  fallback: 'Not available'    // Show if metafield is empty
}
```

### Type-Specific Formatting

#### Date Fields
```javascript
{
  format: {
    format: 'long'  // 'short', 'long', or 'iso'
  }
}

// Examples:
// short: 2/17/2026
// long: February 17, 2026
// iso: 2026-02-17
```

#### Number Fields
```javascript
{
  format: {
    decimals: 2,           // Number of decimal places
    currency: 'USD'        // Format as currency
  }
}

// Examples:
// decimals: 2 → 19.99
// currency: 'USD' → $19.99
```

#### Boolean Fields
```javascript
{
  format: {
    labels: {
      true: 'In Stock',
      false: 'Out of Stock'
    }
  }
}
```

#### JSON Fields
```javascript
{
  format: {
    pretty: true  // Pretty-print JSON
  }
}
```

---

## Real-World Examples

### Example 1: Product Specifications Table

```javascript
import { useMetafields } from '@/utils/metafieldRenderer';

function ProductSpecs({ productId }) {
  const { metafields } = useMetafields('product', productId);
  const specs = metafields.filter(mf => mf.namespace === 'specifications');

  return (
    <table className="w-full">
      <tbody>
        {specs.map(spec => (
          <tr key={spec.key}>
            <td className="font-semibold">{spec.label}</td>
            <td>{spec.value}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}
```

### Example 2: Fabric Care Instructions

```javascript
import MetafieldDisplay from '@/components/puck/MetafieldDisplay';

function ProductCare({ productId }) {
  return (
    <div className="care-instructions">
      <h3>Care Instructions</h3>
      <MetafieldDisplay
        entityType="product"
        entityId={productId}
        namespace="care"
        metafieldKey="washing_instructions"
        displayFormat="html"
        className="prose"
      />
    </div>
  );
}
```

### Example 3: Size Chart Link

```javascript
<MetafieldDisplay
  entityType="product"
  entityId={productId}
  namespace="custom"
  metafieldKey="size_chart_url"
  prefix="📏 "
  suffix=""
  className="text-blue-600 underline cursor-pointer"
  fallbackText="No size chart available"
/>
```

### Example 4: Product Badge from Boolean

```javascript
import { useMetafields } from '@/utils/metafieldRenderer';

function ProductBadge({ productId }) {
  const { getField } = useMetafields('product', productId);
  const isNew = getField('custom', 'is_new_arrival');
  const isOrganic = getField('custom', 'is_organic');

  return (
    <div className="flex gap-2">
      {isNew && <span className="badge badge-new">New</span>}
      {isOrganic && <span className="badge badge-organic">Organic</span>}
    </div>
  );
}
```

---

## API Reference

### `fetchMetafields(ownerType, ownerId)`
Fetch all metafields for an entity.

**Returns:** `Promise<Array>`

### `getMetafield(ownerType, ownerId, namespace, key)`
Get a specific metafield value.

**Returns:** `Promise<any>`

### `renderMetafield(ownerType, ownerId, namespace, key, options)`
Render metafield with template.

**Returns:** `Promise<string>` (HTML)

### `useMetafields(ownerType, ownerId)`
React hook for metafields.

**Returns:** `{ metafields, loading, error, getField, renderField }`

---

## Common Use Cases

| Use Case | Namespace | Key Example | Type |
|----------|-----------|-------------|------|
| Product material | `custom` | `fabric_type` | text |
| Size chart URL | `custom` | `size_chart_url` | text |
| Care instructions | `care` | `washing_instructions` | richtext |
| Dimensions | `specifications` | `dimensions` | text |
| Weight | `specifications` | `weight` | number |
| Is organic | `custom` | `is_organic` | boolean |
| Release date | `custom` | `release_date` | date |
| Technical specs | `specifications` | `tech_specs` | json |

---

## Best Practices

1. **Use namespaces** to organize related metafields (e.g., `specifications`, `care`, `seo`)
2. **Provide fallbacks** for empty metafields to avoid blank spaces
3. **Format appropriately** based on type (dates, numbers, booleans)
4. **Cache metafields** when displaying multiple fields from the same entity
5. **Use descriptive keys** like `fabric_type` instead of `field1`

---

## Troubleshooting

**Metafield not showing?**
- Verify the metafield exists in admin
- Check namespace and key spelling
- Ensure entity ID is correct
- Check browser console for errors

**Wrong format?**
- Verify metafield type matches expected format
- Check formatting options
- Ensure value is not null/undefined

**Performance issues?**
- Use `useMetafields` hook to fetch once
- Avoid fetching same metafields multiple times
- Consider caching for frequently accessed fields
