# @craftercms/ice

> Crafter CMS in-context editing library

Latest version **4.3.1** (published 2026-07-07) · GNU LGPL 3.0 license · 0 weekly downloads

## Install

```sh
npm install @craftercms/ice
pnpm add @craftercms/ice
yarn add @craftercms/ice
bun add @craftercms/ice
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 4.3.1 |
| Published | 2026-07-07 |
| First published | 2020-02-27 |
| Weekly downloads | 0 |
| License | GNU LGPL 3.0 |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 270.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 1 |
| Author | CrafterCMS |
| Maintainers | sumerjabri, rart |
| Keywords | In context editing, content management, crafter cms, craftercms, crafter |

## Links

- npm: https://www.npmjs.com/package/@craftercms/ice
- Repository: https://github.com/craftercms/craftercms-sdk-js
- Homepage: https://github.com/craftercms/craftercms-sdk-js#readme
- Issues: https://github.com/craftercms/craftercms-sdk-js/issues
- npm.io page: https://npm.io/package/@craftercms/ice

## Dependencies (1)

- [@craftercms/models](https://npm.io/package/@craftercms/models.md) ^4.3.0

## Alternatives

- [jsforce](https://npm.io/package/jsforce.md) — 851.2K weekly downloads
- [react-native-qrcode-svg](https://npm.io/package/react-native-qrcode-svg.md) — 693.5K weekly downloads
- [@salesforce/plugin-data](https://npm.io/package/@salesforce/plugin-data.md) — 394.9K weekly downloads
- [@backstage/plugin-search-common](https://npm.io/package/@backstage/plugin-search-common.md) — 308.5K weekly downloads
- [@chain-registry/types](https://npm.io/package/@chain-registry/types.md) — 38.4K weekly downloads

## Recent versions

- 4.3.1 (latest) — 2026-07-07
- 4.2.2-support.1 (latest-v4.2.2) — 2025-03-28
- 4.1.9 (support-4.1.x) — 2024-11-25
- 4.3.0 — 2025-02-20
- 4.2.2 — 2025-01-21
- 4.2.1 — 2024-12-16
- 4.2.0 — 2024-10-24
- 4.1.8 — 2024-09-13
- 4.1.7 — 2024-09-11
- 4.1.6 — 2024-08-20
- 4.1.5 — 2024-06-03
- 4.1.4 — 2024-05-03
- 4.1.3 — 2024-03-12
- 4.1.2-support.2 — 2023-11-17
- 4.1.2-1 — 2023-10-31
- … 19 more at https://npm.io/package/@craftercms/ice/versions

## README

![npm (scoped)](https://img.shields.io/npm/v/@craftercms/ice?style=plastic)

# @craftercms/ice

Contains JavaScript utilities to use Crafter CMS' in-context editing in your apps and sites

**Note**: All methods of this package work with [Content Instance](../models/src/ContentInstance.ts) as data structure they understand (the model param). Use in conjunction with `parseDescriptor` and `preParseSearchResults` from `@craftercms/content` to obtain such data structure.

## Usage

All of Crafter CMS packages can be used either via npm or in plain html/javascript via regular script imports.

### Via npm

- `yarn add @craftercms/ice` or `npm install @craftercms/ice`
- `import` or `require` the functions you wish.

### Via html script imports

- Download the bundle and import them in your page.
- The bundles declare a global variable named `craftercms`. You can access all craftercms' packages and functions under this root.
- The `ice` package depends on rxjs, make sure to import rxjs too before the `ice` script.
 
**Tip**: Once you've imported the scripts, type `craftercms` on your browser's dev tools console to inspect the package(s)
 
#### Vanilla html/js example
 ```html
<div id="myFeature"></div>
<script src="https://unpkg.com/rxjs"></script>
<script src="https://unpkg.com/@craftercms/utils"></script>
<script src="https://unpkg.com/@craftercms/classes"></script>
<script src="https://unpkg.com/@craftercms/content"></script>
<script src="https://unpkg.com/@craftercms/ice"></script>
<script>
  (function ({ content, ice }, { operators }) {

    content.getItem(
      '/site/website/index.xml',
      { baseUrl: 'http://localhost:8080', site: 'editorial' }
    ).pipe(
      operators.map(content.parseDescriptor)
    ).subscribe((model) => {
      const attrs = ice.getICEAttributes({ model, parentModelId: '/site/website/index.xml' });
      const elem = document.querySelector('#myFeature');
      Object.entries(attrs).forEach(([attr, value]) => {
        elem.setAttribute(attr, value);
      });
      Object.entries(model).forEach(([fieldId, value]) => {
        if (fieldId !== 'craftercms') {
          const el = document.createElement('div');
          el.innerText = `${fieldId}: ${value}`;
          elem.appendChild(el);
        }
      });
    });

  })(craftercms, rxjs);
</script>
```

## Package Index

The examples below assume usage in the style of using via npm. If you're using the bundles, 
directly importing as a script in the browser, these functions will be under the global variable
named `craftercms.ice` (i.e. `window.craftercms.ice`).

### fetchIsAuthoring

Interrogates the current origin server to determine if the site/app is running in Crafter CMS authoring environment (Preview). Positive reply (`true`) means is authoring; assume you need to add pencils and import other authoring tools. False means is delivery and all authoring tools should be disabled.

#### Example

```typescript
import { fetchIsAuthoring } from '@craftercms/ice';

fetchIsAuthoring().then((isAuthoring) => {
  console.log(`You're currently in ${isAuthoring ? 'authoring' : 'delivery'}.`);
});
```  

### getIceAttributes

Use this function to obtain the attributes that you need to add to your HTML element(s) to put pencils for In Context Editing.

#### Example

```typescript
  import { from, forkJoin } from 'rxjs';
  import { map } from 'rxjs/operators';
  import { getICEAttributes, fetchIsAuthoring, repaintPencils } from '@craftercms/ice';
  import { getItem, parseDescriptor } from '@craftercms/content';

  forkJoin({
    isAuthoring: from(fetchIsAuthoring()),
    model: getItem('/site/website/index.xml', { site: 'editorial' }).pipe(map(parseDescriptor))
  }).subscribe(({ isAuthoring, model }) => {
  
    const page = document.querySelector('#main');
    if (page) {
      const homepageEditPencil = getICEAttributes({ model, isAuthoring });
      Object.entries(homepageEditPencil).forEach(([attr, value]) => {
        page.setAttribute(attr, value);
      });
      repaintPencils();
    }
  
  });
```

### getDropZoneAttributes

Use this function to obtain the attributes that you need to add to your HTML element(s) to mark them as an item selector drop zone.

#### Example

```typescript
  import { from, forkJoin } from 'rxjs';
  import { map } from 'rxjs/operators';
  import { getICEAttributes, getDropZoneAttributes, fetchIsAuthoring, repaintPencils } from '@craftercms/ice';
  import { getItem, parseDescriptor } from '@craftercms/content';

  forkJoin({
    isAuthoring: from(fetchIsAuthoring()),
    model: getItem('/site/website/index.xml', { site: 'editorial' }).pipe(map(parseDescriptor))
  }).subscribe(({ isAuthoring, model }) => {
  
    const features = document.querySelector('.features');
    if (features) {

      const homepageFeaturesDropZone = getDropZoneAttributes({ model, isAuthoring, zoneName: 'features_o' });
      Object.entries(homepageFeaturesDropZone).forEach(([attr, value]) => {
        features.setAttribute(attr, value);
      });

      const items = features.querySelectorAll('article');
      items.forEach((item) => {
        const pencil = getICEAttributes({ model, isAuthoring });
        Object.entries(pencil).forEach(([attr, value]) => {
          item.setAttribute(attr, value);
        });
      });
        
      repaintPencils();

    }

  });
```

### addAuthoringSupport

Use this method to include the necessary scripts to enable Crafter CMS authoring support (i.e. pencils, drag & drop, etc.) on your site/app. This function does not check whether you're in authoring or delivery. You should check that prior to invoking.

#### Example 

```typescript jsx
import React, { useEffect } from 'react';
import { isAuthoring } from '../utils';
import { addAuthoringSupport } from '@craftercms/ice';

function App() {
  useEffect(() => {
    if (isAuthoring()) {
      // This is a react example but you may use addAuthoringSupport outside of react
      addAuthoringSupport().then(() => {
        // Feel free to discard the promise if you don't need 
        console.log('Authoring tools have loaded and are ready to use.');
      });
    }
  }, []);
  return (
    {/* ... */}
  );
}
```

### repaintPencils

Re-renders all pencils

#### Example

```typescript jsx
import { repaintPencils } from '@craftercms/ice';

new Carousel({
  element: '#myElement',
  // ...other settings
  onSlideChange: () => {
    // Update pencils so current slide pencil is visible and prev slide is gone.
    repaintPencils();
  }
});
```

### reportNavigation

Report up to Crafter Studio that navigation has occurred.

Useful primarily for SPAs when there's no page reload, but the page context changes.

### Example

```typescript
import { reportNavigation } from '@craftercms/ice';

function DynamicRoute(props) {
  const { match, location } = props;
  let url = match.path.includes(':')
    ? match.path.substring(0, match.path.indexOf(':') -1)
    : match.url;
  useEffect(() => {
    reportNavigation(url);
  }, [url]);
  return (
    ...
  );
}
```

## @craftercms/ice/react

React bindings for pencils and drop zones. Requires React 16.8.0 or above (hooks release). Not available for class components, use the plain methods described above part of this same package.

### useICE

Use this function to obtain the attributes that you need to add to your HTML element(s) to put pencils for In Context Editing.

Make sure to use the parseDescriptor function from `@craftercms/content` as that's the structure these hooks understand.

#### Example

```typescript jsx
  import React, { useEffect, useState } from 'react';
  import { from, forkJoin } from 'rxjs';
  import { map } from 'rxjs/operators';
  import { fetchIsAuthoring } from '@craftercms/ice';
  import { useICE } from '@craftercms/ice/react';
  import { getItem, parseDescriptor } from '@craftercms/content';
  
  function App() {
    const [state, setState] = useState();
    useEffect(() => {
      forkJoin({
        isAuthoring: from(fetchIsAuthoring()),
        model: getItem('/site/website/index.xml', { site: 'editorial' }).pipe(map(parseDescriptor))
      }).subscribe(({ isAuthoring, model }) => {
        setState({ isAuthoring, model });
      });
    }, []);
    return (
      state ? <HomePage /> : 'Loading...'
    );
  }
  
  function HomePage(props) {
    const { isAuthoring, model } = props;
    const { props: pencil } = useICE({ isAuthoring, model });
    return  (
      <div {...pencil}>
        {/* ... */}
      </div>
    );
  }
  
```

### useDropZone

Use this function to obtain the attributes that you need to add to your HTML element(s) to mark them as an item selector drop zone.

Make sure to use the parseDescriptor function from `@craftercms/content` as that's the structure these hooks understand.

```typescript jsx
  import React, { useEffect, useState } from 'react';
  import { from, forkJoin } from 'rxjs';
  import { map } from 'rxjs/operators';
  import { fetchIsAuthoring } from '@craftercms/ice';
  import { useICE, useDropZone } from '@craftercms/ice/react';
  import { getItem, parseDescriptor } from '@craftercms/content';
  
  function App() {
    const [state, setState] = useState();
    useEffect(() => {
      forkJoin({
        isAuthoring: from(fetchIsAuthoring()),
        model: getItem('/site/website/index.xml', { site: 'editorial' }).pipe(map(parseDescriptor))
      }).subscribe(({ isAuthoring, model }) => {
        setState({ isAuthoring, model });
      });
    }, []);
    return (
      state ? <HomePage /> : 'Loading...'
    );
  }
  
  function HomePage(props) {
    const { isAuthoring, model } = props;
    const { props: pencil } = useICE({ isAuthoring, model });
    const { props: dz } = useDropZone({ isAuthoring, model, fieldId: 'features_o' });
    return  (
      <div {...pencil}>
        <section className="features" {...dz}>
          {
            model.features_o.map((feature) => <Feature model={feature} isAuthoring={isAuthoring} />)
          }
        </section>
      </div>
    );
  }
  
  function Feature(props) {
    const { isAuthoring, model } = props;
    const { props: pencil } = useICE({ isAuthoring, model });
    return  (
      <article {...pencil}>
        <h2>{model.title_s}</h2>
        <p>{model.body_s}</p>
      </article>
    );
  }
```

---
_Source: https://npm.io/package/@craftercms/ice · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
