# Recipes

## Global page spinner

```tsx
const { isAnyPageLoading } = useLoaderStates();
return isAnyPageLoading ? <FullScreenSpinner /> : null;
```

## Section-level loader

```tsx
const { isPageLoading, withPageLoader } = useLoaderStates();
await withPageLoader(() => fetch("/api/orders"), "orders-section");

return isPageLoading("orders-section") ? <SectionSkeleton /> : <OrdersTable />;
```

## Submit button loader

```tsx
const { withKeyLoader, isKeyLoading } = useLoaderStates();

const submit = () =>
  withKeyLoader("submit-btn", () => api.submitForm(data));

<button onClick={submit} disabled={isKeyLoading("submit-btn")}>
  {isKeyLoading("submit-btn") ? "Submitting..." : "Submit"}
</button>
```

## Multiple independent button loaders

```tsx
await withKeyLoader(`delete-${id}`, () => api.deleteItem(id));
await withKeyLoader(`archive-${id}`, () => api.archiveItem(id));
```

## Compose with toast/error handling

```tsx
try {
  await withKeyLoader("save", () => api.save(payload));
  toast.success("Saved");
} catch (error) {
  toast.error("Save failed");
}
```

## Manual loader around callback-style code

```tsx
registerLoader("legacy-sync");
try {
  await new Promise<void>((resolve, reject) => {
    legacyClient.run((err: unknown) => {
      if (err) reject(err);
      else resolve();
    });
  });
} finally {
  removeLoader("legacy-sync");
}
```
