> For the complete documentation index, see [llms.txt](https://docs.rierino.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rierino.com/design/pro-code/modules/wrappers.md).

# Wrappers

Connection and change wrappers provide built-in mechanisms for managing records

```jsx
import ConnectionWrapper from "operations/connectionwrapper";
import ChangeWrapper from "operations/changewrapper";
```

These are the two pieces that give a standard screen its data contract. On a **custom page**, where nothing is set up for you, wrapping your own component in them is the shortest route to the same behaviour a normal list or editor screen gets.

> Both take **exactly one child element**. They inject props into that child, so a fragment or multiple children will not work.

***

## ConnectionWrapper

Handles everything about talking to a data source: listing, loading a record, create, update, delete, success and error notifications, keeping `?id=` in the URL and resolving version conflicts.

#### Properties

| Prop                               | Notes                                                                  |
| ---------------------------------- | ---------------------------------------------------------------------- |
| `source`                           | **required**, the data source id                                       |
| `noCreate` / `noSave` / `noDelete` | pass `true` to withhold that action (the child receives `null` for it) |
| `namePath`                         | path or expression used to title the record in page history            |
| `noRoute`                          | don't sync the selected record into the URL                            |

#### What your child receives

The full list prop set from Props reference: `itemList`, `item`, `onList`, `onQuery`, `onSelect`, `onNew`, `onSave`, `onDelete`, `onReload`, `onCopy`, `onPaste`, `dispatch`, `sendMessage`.

```jsx
import React from "react";
import ConnectionWrapper from "operations/connectionwrapper";

function Inner(props) {
  React.useEffect(() => {
    props.onQuery({ params: { limit: 50 } });
  }, []);

  return (
    <ul>
      {(props.itemList?.list || []).map((i) => (
        <li key={i.id} onClick={() => props.onSelect(i.id)}>{i.data?.title || i.id}</li>
      ))}
    </ul>
  );
}

export default function Page() {
  return (
    <ConnectionWrapper source="product">
      <Inner />
    </ConnectionWrapper>
  );
}
```

#### Worth knowing

* **A load error replaces your component.** If the list or record request fails, the wrapper renders an error screen instead of your child. You will not get a chance to render a custom empty state for that case.
* **It owns the URL.** The selected record id is written into `?id=` (debounced). If your component also navigates, the two will fight. Set `noRoute` if you want to drive the URL yourself.
* **It shows its own notifications.** Success and failure messages for every save and delete are raised for you; don't duplicate them.
* **Version conflicts are handled for you.** A conflict opens a dialog offering to force the write or merge the changed fields.
* `onCopy` / `onPaste` share an in-memory clipboard across the screen.
* `item._new` tells you whether the current record has ever been saved.

***

## ChangeWrapper

Holds an editable working copy of a record and tracks which fields are dirty. This is the piece that sits between a list and its editor. Use it if you build your own editing flow.

#### Properties

| Prop                      | Notes                                                                                                                                                    |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `item`                    | the record to edit                                                                                                                                       |
| `defaults`                | array of `{ path, value }` applied to new copies. `value` follows the `=expression` convention and can read the current user and preferences via `_meta` |
| `skips`                   | top-level fields to strip from the copy                                                                                                                  |
| `root`                    | provide record context to the subtree (used by nested editors)                                                                                           |
| `extraData` / `extraPath` | enrichment data to fetch and attach to the working copy                                                                                                  |
| `onChange`                | called with the whole updated record on every change                                                                                                     |

#### What your child receives

| Prop            | Notes                                       |
| --------------- | ------------------------------------------- |
| `data`          | the working copy                            |
| `onChange`      | `(value, path)`                             |
| `changes`       | `{ [path]: { value, counter } }`            |
| `changeCounter` | `0` when pristine, handy for disabling Save |

#### Change semantics

* `path === "$"` replaces the whole record.
* Any other path writes into the copy at that path.
* Writing `undefined` or `null` **removes** the field rather than storing an empty value.
* The working copy is rebuilt and `changes` reset, whenever a newly loaded record arrives.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.rierino.com/design/pro-code/modules/wrappers.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
