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.