AppbrewDevelopers
Concepts

State and data

The state layer and the hooks your code reads data through.

All app state (config, products, cart, user, navigation) lives in a single Zustand-based store. You never touch the store directly. @gauntlet/state exposes it through hooks, and blocks read and write everything through them. This keeps blocks decoupled from where data actually comes from: the same hook call is backed by config, Shopify APIs, or Appbrew services as appropriate, with caching handled for you.

The hook families

The names below are the ones you will use most, grouped by what they cover.

Blocks — a block's own config, resolved through the layers described in Config and theme:

  • useBlock(screenId, componentId, instanceId) — the block's raw config entry
  • useBlockSettings(block) — merged source and options (globals + instance)
  • useBlockStyle(block) — merged style (theme + variant + instance)

Products:

  • useProduct, useProductByHandle, useProductsByHandles

Cart and checkout:

  • useCart, useAddToCart, useCheckout

User:

  • useUser — the signed-in customer and authentication state

Settings:

  • useSettings — the app's global settings object
  • useGlobalSettings — global defaults for block types
  • useIntegrationSettings — your integration's config values (see The integrations model)

Navigation:

  • useRoute — the current route
  • useLink — navigate to a screen, product, collection, or URL from config-style links

Shopify queries and metafields:

  • useShopifyQuery — run a raw Storefront GraphQL query
  • useMetafieldQuery, useMultipleMetafields — read metafields from products and other resources

Analytics:

  • useAnalytics — fire events to every registered tracker

Type the result, null-check before use

Hooks that fetch remote data return null (or undefined) until the data arrives, and query hooks are generic: you declare the shape you expect. Both rules together look like this:

import { useShopifyQuery } from '@gauntlet/state'

interface FeaturedProductResult {
  product: {
    title: string
    handle: string
  } | null
}

const query = `
  query {
    product(handle: "featured-hoodie") {
      title
      handle
    }
  }
`

export function FeaturedProduct() {
  const data = useShopifyQuery<FeaturedProductResult>(query)

  if (!data?.product) {
    return null
  }

  return <Text>{data.product.title}</Text>
}

The pattern generalizes to every data hook: define an interface for the result, assert or parameterize the hook with it, and guard before rendering. Rendering null while data loads is correct. A block that assumes data is present crashes the screen for users on slow networks.

Arrays too

The same discipline applies to lists: check the array exists and has items before mapping over it. Config-driven data means any field can be absent on any given app.

Writing state

Mutations go through the same hooks: useAddToCart returns a function that adds line items, useCheckout starts checkout, and so on. Fire-and-forget from event handlers; the store updates and every subscribed block re-renders reactively.

Beyond hooks: actions and events

Hooks are how your UI reads and writes. Actions are the layer underneath: cart:add, checkout:begin, coupon:apply and friends are named operations that anyone can intercept through the app extension host from @gauntlet/brewery (platform 0.27.0 and later). useAddToCart runs cart:add for you.

That matters in two directions. If you are extending someone's app (a partner integration), it is how you change behavior without touching their code. If you are calling an action yourself, note that it returns a Result instead of throwing, so a blocked operation hands you a reason to show the user.

On 0.26 and earlier: the action layer isn't available. Hooks call the built-in operations directly, and a package can't intercept them; behavior changes need a change in app code.

There is also onConfigReady(cb) for running setup once app config has landed. See Extending app behavior.

Full reference

The complete hook catalog with signatures lives at the @gauntlet/state reference.

On this page