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

# Performance

> States what each Analytics Web call costs and which configuration values change it.

A `track` call in Analytics Web adds the event to a queue in memory and returns. The network
request runs later, in the background. This page lists the size of the package, how the queue
sends, how the configuration cache works, and what the React hooks do to limit re-renders.

## Package size

| Build | Size | Where |
| - | - | - |
| Analytics Web core | about 6 KB gzipped | `@grainql/analytics-web` |
| Analytics Web with React hooks | about 8 KB gzipped | `@grainql/analytics-web/react` |
| Runtime dependencies | 0 | `package.json` |

The package is tree-shakeable. A bundler removes the exports that the app does not import.

## Batching

The SDK collects events in a queue and sends the queue as one request. The queue flushes every
`flushInterval` milliseconds, or when it holds `batchSize` events, whichever comes first.

| Value | Default | Effect of a higher value |
| - | - | - |
| `batchSize` | `50` | Fewer requests. Events arrive later. More memory for the queue. |
| `flushInterval` | `5000` | Fewer requests. Events arrive later. `0` turns the timer off. |
| `retryAttempts` | `3` | A failed request keeps trying for longer. |
| `retryDelay` | `1000` | Longer gaps between attempts: 1 s, 2 s, 4 s with the default. |

| Situation | `batchSize` | `flushInterval` |
| - | - | - |
| High traffic | `100` | `10000` |
| Real-time dashboard | `10` | `1000` |
| Serverless function | `1` | `0` |

In a serverless function the process can end before the timer fires. Send each event with
`flush: true` and wait for it.

```typescript theme={null}
await grain.track('order_placed', { orderId: order.id }, { flush: true });
```

On page unload the SDK sends the queue with the Beacon API. The browser completes that request
after the page closes.

Read [Configuration options](/advanced/configuration) for every value.

## Configuration cache

`getConfig` returns a value from memory without a network request. The order is: the value from
the last fetch, then the value in `localStorage`, then `defaultConfigurations`. The SDK fetches
new values in the background and refreshes them every `configRefreshInterval` milliseconds.

| Value | Default | Effect |
| - | - | - |
| `configRefreshInterval` | `300000` | A lower value fetches more often and shows a change sooner. |
| `enableConfigCache` | `true` | `false` skips `localStorage`. Each page load starts from `defaultConfigurations` until the fetch returns. |

Call `preloadConfig` at startup to fetch the keys that the first screen needs.

```typescript theme={null}
await grain.preloadConfig(['hero_text', 'button_color', 'feature_enabled']);
const heroText = grain.getConfig('hero_text');
```

Read [Remote configuration](/core/remote-config) for the cache order.

## React hooks

`useConfig(key)` re-renders the component only when that key changes. `useAllConfigs()`
re-renders when any key changes. Use `useConfig` for one value.

```tsx theme={null}
function Hero() {
  const { value } = useConfig('hero_text');
  return <h1>{value}</h1>;
}
```

`useTrack()` returns a stable function reference. It is safe in a `useCallback` dependency
list, and it does not need memoization of its own.

```tsx theme={null}
function SignupButton() {
  const track = useTrack();
  const handleClick = useCallback(() => {
    track('signup_clicked');
  }, [track]);
  return <button onClick={handleClick}>Sign up</button>;
}
```

Read [React hooks](/react/overview).

## Loading later

Create the client on first use when the first screen does not track anything.

```typescript theme={null}
let grain: GrainAnalytics | null = null;

function getGrain() {
  if (!grain) {
    grain = createGrainAnalytics({ tenantId: 'your-tenant-id' });
  }
  return grain;
}
```

In React, `lazy()` loads the provider in its own chunk. Tracking starts after that chunk loads.

```typescript theme={null}
const GrainProvider = lazy(() =>
  import('@grainql/analytics-web/react').then((m) => ({ default: m.GrainProvider }))
);
```

## Measuring the cost

Set `debug: true` to write batch send times, queue sizes, requests, and retry attempts to the
console. A `track` call takes less than 1 millisecond, because it only adds to the queue.

```typescript theme={null}
const start = performance.now();
grain.track('event', { data: 'value' });
console.log(`track took ${performance.now() - start} ms`);
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.