---
name: mongez-react-atom-presets
description: |
  Pre-built atom shapes — `openAtom` (boolean toggle), `loadingAtom` (loading flag), `fetchingAtom` (full fetch lifecycle), and `portalAtom` (modal/drawer coordinator) — and when to reach for each.
  TRIGGER when: code imports or calls `openAtom`, `loadingAtom`, `fetchingAtom`, or `portalAtom` from `@mongez/react-atom`; code uses `useOpened`, `useLoading`, `useData`, `useError`, `usePagination`, `startLoading`, `stopLoading`, `toggleLoading`, `success`, `failed`, `append`, `prepend`, or `toggle` methods on a preset atom; user asks "how do I make a toggle / open-close atom", "how do I model a loading flag", "how do I do a fetch lifecycle with isLoading/data/error", or "how do I coordinate a modal/drawer"; typical import `import { openAtom, portalAtom } from "@mongez/react-atom"`.
  SKIP: hand-rolling a custom action atom from scratch — use `mongez-react-atom-atoms`; richer cache-keyed server state (invalidation, refetch on focus) belongs in `@mongez/atomic-query`, not `fetchingAtom`; SSR scoping/hydration of preset atoms still uses `mongez-react-atom-ssr`; end-to-end flow examples use `mongez-react-atom-recipes`.
---
# Preset atoms

Four pre-built atom shapes that collapse common patterns to one line.

## `openAtom`

A boolean-shaped toggle with `open`, `close`, `toggle`, and a `useOpened()` hook.

```ts
import { openAtom } from "@mongez/react-atom";

const sidebar = openAtom("sidebar", /* defaultOpened */ false);

// Anywhere:
sidebar.open();
sidebar.close();
sidebar.toggle();

// In components:
function Sidebar() {
  const opened = sidebar.useOpened();
  return opened ? <Drawer /> : null;
}
```

Equivalent to writing:

```ts
const sidebar = atom<boolean, { open(): void; close(): void; toggle(): void; useOpened(): boolean }>({
  key: "sidebar",
  default: false,
  actions: {
    open()   { this.update(true); },
    close()  { this.update(false); },
    toggle() { this.update(!this.value); },
    useOpened() { return (this as any).useValue(); },
  },
});
```

## `loadingAtom`

A boolean with `startLoading` / `stopLoading` / `toggleLoading` verbs.

```ts
import { loadingAtom } from "@mongez/react-atom";

const usersLoading = loadingAtom("usersLoading");

async function fetchUsers() {
  usersLoading.startLoading();
  try {
    const users = await api.users.list();
    setUsers(users);
  } finally {
    usersLoading.stopLoading();
  }
}
```

## `fetchingAtom`

A full fetch lifecycle: `isLoading`, `data`, `error`, optional `pagination`. Comes with `startLoading`, `stopLoading`, `success(data, pagination?)`, `failed(error)`, `append(data)`, `prepend(data)`, plus hooks `useLoading()`, `useData()`, `useError()`, `usePagination()`.

```ts
import { fetchingAtom } from "@mongez/react-atom";

type User = { id: number; name: string };
const usersAtom = fetchingAtom<User[]>("users");

async function loadUsers() {
  usersAtom.startLoading();
  try {
    const { data, pagination } = await api.users.list();
    usersAtom.success(data, pagination);
  } catch (err) {
    usersAtom.failed(err);
  }
}

function UsersList() {
  const isLoading = usersAtom.useLoading();
  const data = usersAtom.useData();
  const error = usersAtom.useError();
  if (isLoading) return <Spinner />;
  if (error) return <ErrorBox e={error} />;
  return <ul>{data?.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
```

For richer cache management, prefer [`@mongez/atomic-query`](https://github.com/hassanzohdy/mongez-atomic-query). `fetchingAtom` is for one-off fetch flows; `queryAtom.useQuery` is for keyed cache.

## `portalAtom`

A coordinator atom for modals, drawers, tooltips, dropdowns. Holds `{ opened: boolean, data: T }` and exposes `open(data?)`, `close()`, `toggle(data?)`, `useOpened()`, `useData()`.

```ts
import { portalAtom } from "@mongez/react-atom";

type DeleteData = { userId: number; userName: string };
const deleteUserModal = portalAtom<DeleteData>("deleteUserModal");

// Anywhere:
deleteUserModal.open({ userId: 7, userName: "Alice" });

function ConfirmDeleteUser() {
  const opened = deleteUserModal.useOpened();
  const { userId, userName } = deleteUserModal.useData();
  if (!opened) return null;
  return (
    <Dialog>
      <p>Delete {userName}?</p>
      <button onClick={() => deleteUserModal.close()}>Cancel</button>
      <button onClick={async () => { await api.users.remove(userId); deleteUserModal.close(); }}>
        Delete
      </button>
    </Dialog>
  );
}
```

Each `portalAtom` key is suffixed with `-portal` internally, so `portalAtom("deleteUser")` creates an atom whose key is `deleteUser-portal`.
