# Sandbox

```ts
import { Sandbox } from 'cx/widgets';
```



Sandbox acts as a data multiplexer. It selects a value from a `storage` object based on a dynamic `key` and exposes it through a `recordAlias`. When the key changes, Sandbox automatically switches to the corresponding data in storage.

## Example

```tsx
import { createModel } from "cx/data";
import { LabelsTopLayout, tpl } from "cx/ui";
import { Radio, Sandbox, TextField } from "cx/widgets";

interface Contestant {
  firstName: string;
  lastName: string;
}

interface PageModel {
  place: string;
  results: Record<string, Contestant>;
  $contestant: Contestant;
}

const m = createModel<PageModel>();

export default (
  <div>
    <div class="flex gap-4">
      <Radio value={m.place} option="winner" default>
        1st Place
      </Radio>
      <Radio value={m.place} option="second">
        2nd Place
      </Radio>
      <Radio value={m.place} option="third">
        3rd Place
      </Radio>
    </div>
    <Sandbox key={m.place} storage={m.results} recordAlias={m.$contestant}>
      <LabelsTopLayout>
        <TextField value={m.$contestant.firstName} label="First Name" />
        <TextField value={m.$contestant.lastName} label="Last Name" />
      </LabelsTopLayout>
    </Sandbox>
    <div class="mt-4 flex flex-col gap-2">
      <div class="font-medium leading-none">Results</div>
      <div
        class="text-sm"
        text={tpl(
          m.results.winner.firstName,
          m.results.winner.lastName,
          "1. {0} {1}",
        )}
      />
      <div
        class="text-sm"
        text={tpl(
          m.results.second.firstName,
          m.results.second.lastName,
          "2. {0} {1}",
        )}
      />
      <div
        class="text-sm"
        text={tpl(
          m.results.third.firstName,
          m.results.third.lastName,
          "3. {0} {1}",
        )}
      />
    </div>
  </div>
);

```

## Typed Model

Add `$contestant` (or your chosen alias) to your model interface:

```tsx
interface PageModel {
  place: string;
  results: Record<string, Contestant>;
  $contestant: Contestant;
}

const m = createModel<PageModel>();
```

Then use `recordAlias={m.$contestant}` to bind the record accessor:

```tsx
<Sandbox key={m.place} storage={m.results} recordAlias={m.$contestant}>
  <TextField value={m.$contestant.firstName} label="First Name" />
</Sandbox>
```

## Sandboxed Routes

Sandbox is commonly used in single-page applications to isolate data belonging to different pages identified by URL parameters. See [Routing](/docs/core/routing) for more info.

```tsx
<Route route="~/user/:userId" url={m.url}>
  <Sandbox key={m.$route.userId} storage={m.users} recordAlias={m.$page}>
    <UserProfile />
  </Sandbox>
</Route>
```

## Configuration

| Property          | Type      | Description                                                                |
| ----------------- | --------- | -------------------------------------------------------------------------- |
| `storage`         | `Prop`    | Storage binding. All data will be stored inside.                           |
| `key`/`accessKey` | `Prop`    | Key value used to access the data from the `storage`.                      |
| `recordAlias`     | `string`  | Alias used to expose local data. Defaults to `$page`.                      |
| `immutable`       | `boolean` | Indicate that the data in the parent store should not be mutated.          |
| `sealed`          | `boolean` | Indicate that the data in the store should not be mutated by child stores. |