npm package: @liberfi.io/ui-chain-select
Description: Chain Management for Liberfi React SDK
Author: liberfi.io
Latest version: 0.1.142

# @liberfi.io/ui-chain-select

Chain selection UI and state management for the Liberfi React SDK. Provides a responsive chain selector widget (desktop popover / mobile icon buttons), Jotai-based state atoms, and hooks for reading and switching the current chain. Consumed by app-level packages and widgets that need chain-aware behaviour.

## Design Philosophy

- **State / Hooks / UI layering** — Jotai atoms own the chain state; hooks read/write atoms and invoke callbacks; the widget wires them together; UI components are purely presentational and can be used independently.
- **Inversion of control** — All side effects (wallet switching, toast, analytics) are delegated to the caller via `onSwitchChain`, `onSuccess`, and `onError` callbacks. The package never hardcodes feedback.
- **Optional Provider** — Works out of the box with default atoms (zero-config). Wrap with `ChainSelectProvider` only when multiple independent chain selectors need to coexist (different `storageKeyPrefix`).
- **Single source of truth** — `Chain` and `ChainNamespace` types come from `@liberfi.io/types`; display names, colors, icons, and `chainToNamespace` come from `@liberfi.io/utils` and `@liberfi.io/ui`.

## Installation

```bash
pnpm add @liberfi.io/ui-chain-select
```

### Peer dependencies

The consumer must provide:

| Package          | Version   |
| ---------------- | --------- |
| `react`          | >= 18     |
| `react-dom`      | >= 18     |
| `jotai`          | >= 2.15.1 |
| `@liberfi.io/ui` | workspace |

## API Reference

### Components

#### `ChainSelectWidget`

Full-featured chain selector that wires state and callbacks. Drop-in usage — no Provider required.

```tsx
function ChainSelectWidget(props: ChainSelectWidgetProps): JSX.Element;
```

| Prop            | Type                              | Default                       | Description                         |
| --------------- | --------------------------------- | ----------------------------- | ----------------------------------- |
| `size`          | `"sm" \| "md" \| "lg"`            | `undefined`                   | Button / item size                  |
| `className`     | `string`                          | `undefined`                   | Class on the root element           |
| `candidates`    | `Chain[]`                         | `[SOLANA, ETHEREUM, BINANCE]` | Chains to offer                     |
| `onSwitchChain` | `(chain: Chain) => Promise<void>` | `undefined`                   | Async handler (e.g. wallet switch)  |
| `onSuccess`     | `(chain: Chain) => void`          | `undefined`                   | Called after successful switch      |
| `onError`       | `(error: unknown) => void`        | `undefined`                   | Called when `onSwitchChain` rejects |

#### `ChainSelectUI`

Presentational responsive shell. Renders `ChainSelectDesktopUI` or `ChainSelectMobileUI` based on viewport.

```tsx
function ChainSelectUI(props: ChainSelectUIProps): JSX.Element;
```

| Prop            | Type                                      | Default              | Description               |
| --------------- | ----------------------------------------- | -------------------- | ------------------------- |
| `size`          | `"sm" \| "md" \| "lg"`                    | `undefined`          | Button / item size        |
| `chain`         | `Chain`                                   | `candidates[0]`      | Currently selected chain  |
| `candidates`    | `Chain[]`                                 | `DEFAULT_CANDIDATES` | Chains to show            |
| `onSelectChain` | `(chain: Chain) => void \| Promise<void>` | `undefined`          | Selection handler         |
| `isSwitching`   | `boolean`                                 | `undefined`          | Shows loading state       |
| `className`     | `string`                                  | `undefined`          | Class on the root element |

#### `ChainAvatar`

Renders a chain icon. Uses dedicated SVG icons for known chains, falls back to an `Avatar` with the chain's icon URL.

```tsx
function ChainAvatar(props: ChainAvatarProps): JSX.Element;
```

| Prop        | Type     | Description                |
| ----------- | -------- | -------------------------- |
| `chain`     | `Chain`  | The chain to display       |
| `className` | `string` | Class on the icon / avatar |

#### `ChainSelectProvider`

Optional context provider for multi-instance scenarios.

```tsx
function ChainSelectProvider(props: ChainSelectProviderProps): JSX.Element;
```

| Prop               | Type        | Default | Description                  |
| ------------------ | ----------- | ------- | ---------------------------- |
| `storageKeyPrefix` | `string`    | `""`    | Prefix for localStorage keys |
| `children`         | `ReactNode` | —       | Children                     |

### Hooks

#### `useSelectChain`

Returns a `selectChain` function and `isSwitching` state.

```typescript
function useSelectChain(options?: UseSelectChainOptions): {
  selectChain: (chain: Chain) => Promise<void>;
  isSwitching: boolean;
};
```

| Option          | Type                              | Description                         |
| --------------- | --------------------------------- | ----------------------------------- |
| `onSwitchChain` | `(chain: Chain) => Promise<void>` | Async handler before state update   |
| `onSuccess`     | `(chain: Chain) => void`          | Called after successful switch      |
| `onError`       | `(error: unknown) => void`        | Called when `onSwitchChain` rejects |

#### `useCurrentChain`

Reads the current chain and namespace from state.

```typescript
function useCurrentChain(): {
  chain: Chain;
  chainNamespace: ChainNamespace;
};
```

### State (Jotai atoms)

| Export               | Type                              | Description                                     |
| -------------------- | --------------------------------- | ----------------------------------------------- |
| `chainAtom`          | `WritableAtom<Chain>`             | Default chain atom (key `"chain"`)              |
| `chainNamespaceAtom` | `WritableAtom<ChainNamespace>`    | Default namespace atom (key `"chainNamespace"`) |
| `defaultChainAtoms`  | `ChainAtoms`                      | Bundle of both default atoms                    |
| `createChainAtoms`   | `(prefix?: string) => ChainAtoms` | Factory for custom-prefixed atoms               |

### Types

| Export                         | Description                         |
| ------------------------------ | ----------------------------------- |
| `ChainAtoms`                   | `{ chainAtom, chainNamespaceAtom }` |
| `ChainSelectStateContextValue` | Context value type                  |
| `ChainSelectProviderProps`     | Props for `ChainSelectProvider`     |
| `ChainSelectWidgetProps`       | Props for `ChainSelectWidget`       |
| `ChainSelectUIProps`           | Props for `ChainSelectUI`           |
| `ChainAvatarProps`             | Props for `ChainAvatar`             |
| `UseSelectChainOptions`        | Options for `useSelectChain`        |

## Usage Examples

### Basic usage

```tsx
import { ChainSelectWidget } from "@liberfi.io/ui-chain-select";

function App() {
  return (
    <ChainSelectWidget
      onSwitchChain={async (chain) => {
        await wallet.switchChain(chain);
      }}
      onSuccess={(chain) => toast.success(`Switched to ${chain}`)}
      onError={(err) => toast.error("Switch failed")}
    />
  );
}
```

### Presentational-only (no state management)

```tsx
import { Chain } from "@liberfi.io/types";
import { ChainSelectUI } from "@liberfi.io/ui-chain-select";

function ReadOnlyChainDisplay() {
  return (
    <ChainSelectUI
      chain={Chain.ETHEREUM}
      candidates={[Chain.ETHEREUM, Chain.BINANCE]}
      onSelectChain={(chain) => console.log(chain)}
    />
  );
}
```

### Multiple independent instances

```tsx
import {
  ChainSelectProvider,
  ChainSelectWidget,
} from "@liberfi.io/ui-chain-select";

function MultiChainApp() {
  return (
    <>
      <ChainSelectProvider storageKeyPrefix="source_">
        <ChainSelectWidget onSwitchChain={handleSourceSwitch} />
      </ChainSelectProvider>
      <ChainSelectProvider storageKeyPrefix="target_">
        <ChainSelectWidget onSwitchChain={handleTargetSwitch} />
      </ChainSelectProvider>
    </>
  );
}
```

### Reading current chain from hooks

```tsx
import { useCurrentChain } from "@liberfi.io/ui-chain-select";

function ChainInfo() {
  const { chain, chainNamespace } = useCurrentChain();
  return (
    <p>
      Current: {chain} ({chainNamespace})
    </p>
  );
}
```

## Future Improvements

- Refactor `version.ts` module-level side effect to a lazy registration pattern (monorepo-wide change).