Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.uselayers.com/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers best practices for SDK setup, controller lifecycle management, and common mistakes to avoid.

SDK initialization

Create client once

Create a single client instance and share it across your application. Creating multiple instances wastes resources and breaks caching.
// ❌ Bad - creates multiple instances
function SearchComponent() {
  const { data: client } = createClient({ 
    token: 'your-token',
    sorts: [{ name: 'Featured', code: 'featured' }],
    facets: [{ name: 'Color', code: 'options.color' }],
  })
  // Creates new client instance on every render
}

// ✅ Good - create once, share globally
const { data: client } = createClient({ 
  token: 'your-token',
  sorts: [{ name: 'Featured', code: 'featured' }],
  facets: [{ name: 'Color', code: 'options.color' }],
})

if (client) {
  function SearchComponent() {
    const search = client.search()
    // ...
  }
}

Required configuration

Always provide sorts and facets when creating the client:
const { data: client, error } = createClient({
  token: 'your-token',
  sorts: [
    { name: 'Featured', code: 'featured' },
    { name: 'Price: Low to High', code: 'price_asc' },
    { name: 'Price: High to Low', code: 'price_desc' },
  ],
  facets: [
    { name: 'Color', code: 'options.color' },
    { name: 'Size', code: 'options.size' },
    { name: 'Vendor', code: 'vendor' },
    { name: 'Product Type', code: 'product_type' },
  ],
})

Controller lifecycle

Always dispose controllers

Controllers maintain subscriptions and state. Dispose them when components unmount to prevent memory leaks.
// ❌ Bad - memory leak
function SearchComponent() {
  const search = client.search()
  // Component unmounts but controller keeps running
}

// ✅ Good - cleanup on unmount
function SearchComponent() {
  const search = client.search()
  
  onUnmount(() => search.dispose()) // framework-specific cleanup
}

Controller reuse

Controllers can be reused for multiple operations:
const search = client.search()

// Use the same controller for multiple searches
await search.execute({ query: 'ring' })
await search.execute({ query: 'necklace' })
await search.execute({ query: 'bracelet' })

// Dispose when done
search.dispose()

Filter handling

Use filter DSL for complex filters

For complex filter conditions, use the filter DSL:
import { filter, and, eq } from '@commerce-blocks/sdk'

// Use filter DSL for explicit control
const collection = client.collection({ handle: 'shirts' })
await collection.execute({ 
  filters: filter(and(
    eq('options.color', 'Red'),
    eq('options.size', 'M')
  ))
})

Error handling

Handle errors gracefully

Always check for errors in the Result object:
const search = client.search()
const result = await search.execute({ query: 'ring' })

if (result.error) {
  switch (result.error._tag) {
    case 'NetworkError':
      showError('Connection issue. Please try again.')
      break
    case 'ApiError':
      if (result.error.status === 429) {
        showError('Too many requests. Please wait a moment.')
      }
      break
    case 'ValidationError':
      showError('Invalid search parameters.')
      break
  }
  return
}

// Safe to use result.data
const products = result.data.products

Prepare failures are non-fatal

If prepare fails, execute will still work (but may be slower):
const search = client.search()
const prepareResult = await search.prepare({ query: 'ring' })

if (prepareResult.error) {
  console.warn('Prepare failed, execute will work but may be slower')
}

// Execute still works
const result = await search.execute({ query: 'ring' })

Next steps

Search Patterns

Learn advanced search patterns including prepare/execute flows.

Caching

Understand SDK caching behavior and optimization.