# Connect your data

> Part of the documentation that comes with Beamline's complete system (docs/DATA-INTEGRATION.md in the kit). Your coding agent can do all of this for you through Beamline's MCP server: [connect it](https://beamline.io/connect).

Components never fetch. They take plain data through props and report what people do through callbacks; your app
owns the requests, the persistence and the decisions. This page shows the shapes and where your backend plugs in.

## The rules every component follows

- **Data in, events out.** `rows`, `bids`, `candles`, `items` come in as props; `onValueChange`, `onSelect`,
  `onSubmit`, `onApprove` go out. Stateful props have both forms: `value` + `onValueChange` (you hold the state) or
  `defaultValue` (the component holds it).
- **Your handler decides the outcome.** Actions that stand for an external operation (send, approve, place an order,
  delete) accept a handler that may return a promise. The component shows *pending* while it runs, *done* when it
  resolves, and *failed* with your error when it rejects. It never shows success on a timer.
- **Missing is not zero.** Pass `null` for a value you do not have: it renders as unmeasured (hatched), never as 0.
- **Numbers stay raw.** Pass numbers, and a `format(value)` function where the default formatting does not fit.
  Prices keep their precision.

## Market data (trading)

Types and formatters live in `@beamline/ui/lib/market`:

```ts
import type { Candle, Level, Trade, MarketFeed, FeedRequest, UseMarketFeed, Ticker, Position } from "@beamline/ui/lib/market";
```

Single components take the shapes directly: `<OrderBook bids={bids} asks={asks} />`, `<PriceChart candles={candles} />`,
`<TradesTape trades={trades} />`.

The trading terminal takes a **feed hook** you write around your exchange stream. It is called once per open market
(a new symbol or interval remounts it) and returns the current `MarketFeed` on every update:

```tsx
import { useEffect, useState } from "react";
import type { MarketFeed, UseMarketFeed } from "@beamline/ui/lib/market";

export const useExchangeFeed: UseMarketFeed = ({ symbol, intervalSec }) => {
  const [feed, setFeed] = useState<MarketFeed>({ candles: [], bids: [], asks: [], trades: [], last: 0, prev: 0, change: 0 });
  useEffect(() => {
    const socket = new WebSocket(`wss://your-exchange.example/stream?symbol=${symbol}&interval=${intervalSec}`);
    socket.onmessage = (e) => setFeed((prev) => applyUpdate(prev, JSON.parse(e.data))); // your mapping
    return () => socket.close();
  }, [symbol, intervalSec]);
  return feed;
};

```

Trading goes through an **account** you implement on your exchange's order API (`TradingAccount` in
`@beamline/ui/lib/market`): the balance, positions and resting orders you hold in state, and the calls that change them.
A call that fails rejects with an `Error`; its message appears in the order ticket as is. The terminal never fills an
order itself.

```tsx
import type { TradingAccount } from "@beamline/ui/lib/market";

function useExchangeAccount(): TradingAccount {
  const { balance, positions, orders, fills } = useAccountStream(); // your private socket or polling
  return {
    balance, positions, orders, fills,
    submitOrder: async (o) => {
      const res = await api.post("/orders", o); // map OrderRequest to your API
      return res.filled ? { status: "filled", price: res.avgPrice, size: res.filledSize } : { status: "resting", order: res.order };
    },
    cancelOrder: (id) => api.delete(`/orders/${id}`),
    closePosition: (symbol) => api.post(`/positions/${symbol}/close`).then((r) => ({ status: "filled", price: r.avgPrice, size: r.size })),
  };
}

<TradingTerminal symbols={tickers} useFeed={useExchangeFeed} account={useExchangeAccount()} leverage={10} />
```

`useMockFeed` from `@beamline/ui/demo/market-simulator` produces the feed shape from a random walk, and `usePaperAccount`
from `@beamline/ui/demo/paper-account` is a paper-trading account that fills orders against it in the browser. The
reference app and gallery use both, and they are labelled as simulated wherever they appear.

## Tables and records

`DataGrid` takes `rows` and typed `columns` (`text`, `number`, `currency`, `percent`, `date`, `status`, `tags`), with
sorting, filtering, pinning, resizing, inline editing (`onRowsChange`) and footer aggregates. The CRM screen shows a
full example; `@beamline/ui/demo/crm-fixtures` generates its sample deals.

## Streaming text (AI)

`Response` renders markdown that is still arriving, `useSmoothText` (`@beamline/ui/hooks/use-smooth-text`) evens out
bursty token delivery. Feed them the text you have received so far, and a `streaming` flag while more is coming.

## Blocks

Each block's props are documented in `catalog/index.html` and in its `source/ui/blocks/<id>/meta.json`. The
reference app wires every block to an adapter in `reference-app/src/data/`; replace an adapter's body with calls to
your API and keep the shape.

## Personal profile and continued edits

The ordinary Settings configuration uses `name`, `email`, `timeZone` and `notifications`. In the matching 0.2 release, omit `workspace` and `slug` to omit Workspace. Optional extra inputs are declared by `profileFields` and stored under `values.profile`; their changed keys use `profile.<id>`. An optional Job title can therefore use the existing save path instead of a second form or handler. Empty optional values are valid. Inspect the installed types before applying these newer fields to an older artifact.

`onSave(values, changed)` resolves only after the destination has persisted that record and rejects when it has not. The block trims name/email before invoking it. A general Error retains the draft and retry. An Error carrying a known `field` key adds an inline error and focuses that field, or offers a return to its section. Edits made while the promise is pending remain a new unsaved draft after that snapshot succeeds. The callback currently returns `void`; if your server canonicalizes other fields, coordinate the saved-value contract rather than silently displaying a different record.

`createBrowserSettingsApi` in `demo/settings-fixtures` is a labelled sample adapter: it writes the current origin's `localStorage` under `beamline-profile-sample-v1` by default. Reload in the same browser/origin retains its fictional record; clearing browser storage removes it, and another browser/account does not share it. The old `createSettingsApi` remains an in-memory demonstration for specialized workspace sections, so reload resets those changes. Replace either with the application's existing handler for real product data.

For an API adapter, preserve its existing endpoint and request mapping, check the response status and reject on failures. Do not mark the UI successful before the operation resolves. Keep a failed draft available for retry; validation must also protect the actual persistence boundary. Verify the stored result after reload, rejection without mutation, and a controlled failure at that real boundary. A toast proves only the UI response.

When embedded in a host that already supplies navigation, matching Settings releases use `navigation="tabs"` and `heading={null}`. When the host routes sections, use controlled `section` with `navigation="none"`. These choices prevent an accidental second application shell.
