# Migrating from HyperclayJS

> Update a document from HyperclayJS to ClayJS: replace the preset script tag, load plugins and satellites, and update ignored attributes and API calls.

Source: https://clayjs.com/docs/migrating-from-hyperclayjs/

ClayJS replaces HyperclayJS. Updating an existing document involves swapping the loader script tag, loading only the [plugins](https://clayjs.com/docs/plugins/) and [satellites](https://clayjs.com/docs/satellites/) your document uses, replacing ignored attributes, and pointing JavaScript calls at [`window.clay`](https://clayjs.com/docs/api/).

## Replace the script tag and preset

HyperclayJS loaded as an ES module with a `?preset=` query parameter. ClayJS loads as a classic `<script>` tag (never `type="module"`) and takes explicit `?plugins=` and `?exclude=` lists, with optional satellites loaded as separate classic `<script>` tags.

Before (HyperclayJS):

```html
<script src="https://cdn.jsdelivr.net/npm/hyperclayjs@latest/src/hyperclay.js?preset=smooth-sailing" type="module"></script>
```

After (ClayJS):

```html
<script src="https://clayjs.com/v1/clay.js?plugins=sync,sortable,undo,indicator&exclude=richclay"></script>
<script src="https://clayjs.com/v1/clay-options.js"></script>
<script src="https://clayjs.com/v1/clay-ui.js"></script>
```

> [!WARNING]
> Do not put `type="module"` on `clay.js` or any satellite tag. The loader reads `document.currentScript.src` to locate its modules, and logs an error without booting if loaded as a module.

Instead of presets, pick the pieces your document needs:

- **Default-on plugins**: [`richclay`](https://clayjs.com/docs/plugins/richclay/) (`editable` rich text) and [`source`](https://clayjs.com/docs/plugins/source/) (byte-preserving saves). Pass `&exclude=richclay` when your document does not use rich text.
- **Opt-in plugins** (`?plugins=`): [`sync`](https://clayjs.com/docs/plugins/live-sync/), [`live`](https://clayjs.com/docs/plugins/live-sync/), [`sortable`](https://clayjs.com/docs/plugins/sortable/), [`undo`](https://clayjs.com/docs/plugins/undo/), `indicator`, [`upload`](https://clayjs.com/docs/plugins/upload/), [`quickcrop`](https://clayjs.com/docs/plugins/upload/), [`cms`](https://clayjs.com/docs/plugins/cms/), [`people`](https://clayjs.com/docs/plugins/people/), [`visitor-drafts`](https://clayjs.com/docs/plugins/people/), [`message`](https://clayjs.com/docs/plugins/message/), [`wire`](https://clayjs.com/docs/plugins/wire/), [`ai-edit`](https://clayjs.com/docs/plugins/ai-edit/), and `demo`.
- **Satellites** (separate `<script>` tags): `clay-ui.js`, `clay-options.js`, [`clay-events.js`](https://clayjs.com/docs/dom-helpers/), [`clay-dom.js`](https://clayjs.com/docs/dom-helpers/), [`all.js`](https://clayjs.com/docs/dom-helpers/), `clay-utils.js`, [`clay-data.js`](https://clayjs.com/docs/plugins/data/), [`clay-internals.js`](https://clayjs.com/docs/internals/), and [`sap.js`](https://clayjs.com/docs/plugins/sap/).

## Root attributes and edit-mode visibility

Old HyperclayJS files often saved `<html editmode="false">` and hand-written `option-visibility-styles` style tags into the file. ClayJS removes `editmode`, `pageowner`, and `savestatus` from `<html>` on every save.

Because `editmode` is absent before ClayJS boots, any CSS rule written for `html[editmode="false"]` will not match at first paint. Key visitor CSS on `html:not([editmode="true"])` instead, and load `clay-options.js` for `show-when:editmode="true"` (`option:editmode="true"` still works as an alias).

Before (HyperclayJS):

```html
<button option:editmode="true" trigger-save>Save Changes</button>
```

After (ClayJS):

```html
<style>
  html:not([editmode="true"]) [show-when\:editmode="true"] { display: none }
</style>
<button type="button" show-when:editmode="true" trigger-save>Save Changes</button>
```

## Attributes ClayJS keeps, renames, or ignores

Core attributes continue to work: [`persist`](https://clayjs.com/docs/attributes/), `trigger-save`, `autosave`, [`editmode:contenteditable`](https://clayjs.com/docs/edit-mode/), `editmode:onclick`, `editmode:resource`, `viewmode:disabled`, `viewmode:readonly`, [`onbeforesave`, `onbeforesnapshot`, and `onaftersave`](https://clayjs.com/docs/save-attributes/), and [`sortable`](https://clayjs.com/docs/plugins/sortable/) (when `?plugins=sortable` is loaded).

For [regions](https://clayjs.com/docs/regions/), replace `clay="no-save no-watch"` on runtime UI chrome with `clay="editor-ui"`, which bundles `no-data no-save no-snapshot no-watch no-undo`. Legacy region tokens still work as aliases: `save-remove` (`no-save no-undo`), `save-ignore` (`no-dirty no-undo`), `save-freeze` (`freeze no-undo`), `mutations-ignore` (`no-watch`), and `snapshot-remove` (`no-snapshot`).

ClayJS ignores several attributes from older HyperclayJS documents ([Attributes from older Hyperclay files](https://clayjs.com/docs/guide/components/#attributes-from-older-hyperclay-files)):

| Ignored attribute | ClayJS replacement |
| --- | --- |
| `prevent-enter` | A `keydown` listener that checks `event.key === "Enter"`, calls `event.preventDefault()`, and blurs the target |
| `autosize` | CSS `field-sizing: content` |
| `movable`, `movable-handle` | Pointer event handlers that write `data-x` and `data-y` attributes |
| `send-message`, `ajax-form`, `ajax-button` | The [`message`](https://clayjs.com/docs/plugins/message/) plugin (`?plugins=message` with `<form message-form="Thanks, I'll reply soon">`) |
| `upgrade` | Remove from markup |

## JavaScript API and satellites

Replace `window.hyperclay` calls with [`window.clay`](https://clayjs.com/docs/api/), and wrap startup code in `clay.start(fn)` so it runs once after `clay.ready`.

### Undo

When `?plugins=undo` is loaded, [`clay.undo`](https://clayjs.com/docs/plugins/undo/) replaces `hyperclay.undo`.

Before (HyperclayJS):

```js
hyperclay.undo.commit('Add row', () => {
  table.append(newRow)
})
```

After (ClayJS):

```js
clay.undo.commit("Add row", () => {
  table.append(newRow);
});
```

### DOM helpers and All

Element helpers (`this.nearest`, `this.val`, `this.text`, `this.exec`) now live in the `clay-dom.js` [satellite](https://clayjs.com/docs/dom-helpers/), and custom event attributes (`onrender`, `onclone`, `onclickaway`, `onclickchildren`, `onmutation`, `onglobalmutation`) live in `clay-events.js`.

The collection helper in `all.js` is now `All` (with an uppercase `A`, attached to `window.All` and `clay.All`) instead of `all`.

Before (HyperclayJS):

```html
<button onclick="all.panel.classList.toggle('expanded')">Toggle All Panels</button>
```

After (ClayJS, with `all.js` loaded):

```html
<script src="https://clayjs.com/v1/all.js"></script>
<button type="button" onclick="All.panel.classList.toggle('expanded')">Toggle All Panels</button>
```

### Dialogs and toasts

Load `https://clayjs.com/v1/clay-ui.js` for built-in dialogs and notifications on `window.clay`: `clay.ask`, `clay.confirm`, `clay.tell`, `clay.snippet`, `clay.toast`, `clay.toastPersistent`, and `clay.modal`. Note that `clay.confirm(prompt)` rejects with `undefined` when cancelled, so always pass a rejection handler: `clay.confirm("Delete this item?").then(remove, () => {})`.
