---
url: /en/docs/api.md
description: >-
  JavaScript API reference for Sveltia CMS with initialization, components,
  events, and customization.
---

# JavaScript API

Sveltia CMS provides a flexible, client-side JavaScript/TypeScript API that allows developers to customize and extend its functionality. This document provides an overview of the main API components and how to use them.

## Accessing the `CMS` Object

The main entry point for the Sveltia CMS JavaScript API is the global `CMS` object. This object exposes various methods for initializing the CMS, registering custom components, and interacting with the CMS programmatically.

There are two primary ways to access the `CMS` object: via a CDN build or by installing the NPM package.

### Using the CDN

`CMS` is exposed as a global variable when using the UNPKG CDN build. You can access it directly in your scripts:

```html
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js"></script>
<script>
  CMS.init({ config });
  CMS.registerPreviewStyle(filePath);
  CMS.registerEditorComponent(definition);
</script>
```

Alternatively, you can use the ES module version, which can be imported using the `mjs` file extension:

```html
<script type="module">
  import CMS from 'https://unpkg.com/@sveltia/cms/dist/sveltia-cms.mjs';

  CMS.init({ config });
  CMS.registerPreviewStyle(filePath);
  CMS.registerEditorComponent(definition);
</script>
```

### Using the NPM Package

The NPM package is a separate build of the app for use with a build tool like [Vite](https://vite.dev/) or [webpack](https://webpack.js.org/), which doesn’t load any files from CDNs. See [CDN or NPM Package](/en/docs/releases#cdn-or-npm-package) for how it differs from the CDN builds.

Install the `@sveltia/cms` package via your preferred package manager:

::: code-group

```bash [npm]
npm install @sveltia/cms
```

```bash [yarn]
yarn add @sveltia/cms
```

```bash [pnpm]
pnpm add @sveltia/cms
```

```bash [bun]
bun add @sveltia/cms
```

:::

Then, import the `CMS` object in your script to access the initialization and other API methods:

```js
import CMS from '@sveltia/cms';

CMS.init({ config });
CMS.registerPreviewStyle(filePath);
CMS.registerEditorComponent(definition);
```

or import only the methods you need:

```js
import { init } from '@sveltia/cms';

init({ config });
```

TypeScript types are included in the package, so if you edit your project with a TypeScript-aware editor like VS Code, you should get type checking and autocompletion without any additional setup.

## Available Methods

Currently, the following methods are available on the `CMS` object:

* [Manual Initialization](/en/docs/api/initialization): `init`
* [Custom Preview Styles](/en/docs/api/preview-styles): `registerPreviewStyle`
* [Custom Preview Templates](/en/docs/api/preview-templates): `registerPreviewTemplate`
* [Custom Editor Components](/en/docs/api/editor-components): `registerEditorComponent`
* [Custom Field Types](/en/docs/api/field-types): `registerFieldType` (alias: `registerWidget`), `getFieldType` (alias: `getWidget`)
* [Custom File Formats](/en/docs/api/file-formats): `registerCustomFormat`
* [Event Hooks](/en/docs/api/events): `registerEventListener`
* [Rendering Markdown](/en/docs/api/rendering-markdown): `renderRichText`

::: warning Breaking changes from Netlify/Decap CMS

The methods other than those listed above are not supported in Sveltia CMS. This includes:

* `registerLocale`: Sveltia CMS automatically detects and uses the browser’s language settings for localization. No manual registration of locales is necessary.
* `registerRemarkPlugin`: Sveltia CMS uses the Lexical framework for Markdown processing instead of Remark. Therefore, Remark plugins are not compatible.
* All other undocumented methods, including custom backends and custom media storage providers. We may support these features in the future, but our implementation would likely be incompatible with Netlify/Decap CMS.

:::

## Writing React Components

For [Custom Preview Templates](/en/docs/api/preview-templates), [Custom Editor Components](/en/docs/api/editor-components) and [Custom Field Types](/en/docs/api/field-types), you can use React components to create rich, interactive previews and editor interfaces. Sveltia CMS supports three ways to write the markup of these components, and the examples throughout the documentation show all of them:

* [HTM](#using-htm) — HTML-like markup in a tagged template literal. It works without a build step, so it’s the recommended way.
* [JSX](#using-jsx) — The familiar React syntax, which requires a build step.
* [`h()`](#using-h) — Plain function calls, compatible with Netlify/Decap CMS.

A component can be a class component, a function component, or a component wrapped with `memo` or `forwardRef`. Sveltia CMS bundles its own copy of React 19 to render them, so write your components for React 19. That copy of React is available as `CMS.React`, and as the `React` export of the NPM package, giving you the full React API, including [hooks](#using-hooks).

### Using HTM

[HTM](https://github.com/developit/htm) lets you write JSX-like markup in a standard JavaScript [tagged template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates), so your components run in the browser as is, with no transpiler involved. Sveltia CMS bundles HTM and exposes the `html` tag, bound to its copy of React, on the `window` object, so no imports are necessary to use it, even if you install the NPM package:

```js
const Greeting = ({ name }) => html`<p class="greeting">Hello, ${name}!</p>`;
```

The syntax is close to JSX, with a few differences:

* Embed a value with `${}` instead of `{}`, both in text and in attributes, e.g. `<input value=${value} />`. An attribute can also mix static text and values, e.g. `style="color: ${color}"`.
* Embed a component the same way, e.g. `<${Badge} label="New" />`. Close it with `<//>` or `</${Badge}>`.
* Spread props with `...${props}`, e.g. `<input ...${props} />`.
* A template can have multiple root elements, in which case it returns an array of them, so you don’t need a fragment.

The template creates React elements, so props are the same as in JSX: event handlers are named `onClick`, `onChange` and so on, and a component receives whatever you pass. For convenience, you can also use HTML attribute names and values that React doesn’t accept in JSX: `class` and `for` work in place of `className` and `htmlFor`, and the `style` attribute can be a CSS string, e.g. `style="margin: 0; color: red"`, in addition to an object.

See the [HTM documentation](https://github.com/developit/htm#syntax-like-jsx-but-also-lit) for the full syntax.

### Using JSX

Sveltia CMS does not provide a built-in JSX transpiler. To use JSX syntax, you need a build step to transpile it to JavaScript, such as [Vite](https://vitejs.dev/).

### Using `h()`

Netlify/Decap CMS documents components written with plain `createElement()` calls, without any markup. For compatibility, Sveltia CMS exposes a few shorthands globally: `h` (and its alias `createElement`) and `createClass`, plus `rf` for `React.Fragment`:

* `h` (or `createElement`) — An alias for `CMS.React.createElement()`, used to create React elements in the `h()` examples
* `rf` — An alias for `CMS.React.Fragment`, used to create React fragments in the `h()` examples
* `createClass` — Used to define React class components without the `class` syntax. It comes from the [`create-react-class`](https://www.npmjs.com/package/create-react-class) package, as React itself no longer provides it

These are available on the `window` object when Sveltia CMS is loaded, so no imports are necessary to use them. React itself isn’t a global, so it can’t clash with another copy of React your page may load; use `CMS.React` for anything else.

Define the methods you pass to `createClass`, such as `render`, as function expressions rather than arrow functions. `createClass` binds each method to the component instance, which an arrow function doesn’t allow, so `this.props` would be undefined within it. Any other function, including a callback within a method, can be an arrow function.

See [React Without JSX](https://legacy.reactjs.org/docs/react-without-jsx.html) for more information on how to use React without JSX.

### Using Hooks

Function components can use React hooks such as `useState` and `useEffect`, as long as the hooks come from the copy of React bundled with Sveltia CMS — `CMS.React`, or the `React` export of the NPM package:

::: code-group

```js [CDN]
const { useState, useEffect } = CMS.React;
```

```js [NPM]
import CMS, { React } from '@sveltia/cms';

const { useState, useEffect } = React;
```

:::

For example, the following control for a [custom field type](/en/docs/api/field-types) keeps whether its text area is expanded in state:

::: code-group

```js [HTM]
const { useState } = CMS.React;

const NotesControl = ({ forID, classNameWrapper, value, onChange }) => {
  const [expanded, setExpanded] = useState(false);

  return html`
    <textarea
      id=${forID}
      class=${classNameWrapper}
      rows=${expanded ? 12 : 3}
      value=${value ?? ''}
      onChange=${(event) => onChange(event.target.value)}
    />
    <button type="button" onClick=${() => setExpanded(!expanded)}>
      ${expanded ? 'Collapse' : 'Expand'}
    </button>
  `;
};

CMS.registerFieldType('notes', NotesControl);
```

```jsx [JSX]
const { useState } = CMS.React;

const NotesControl = ({ forID, classNameWrapper, value, onChange }) => {
  const [expanded, setExpanded] = useState(false);

  return (
    <>
      <textarea
        id={forID}
        className={classNameWrapper}
        rows={expanded ? 12 : 3}
        value={value ?? ''}
        onChange={(event) => onChange(event.target.value)}
      />
      <button type="button" onClick={() => setExpanded(!expanded)}>
        {expanded ? 'Collapse' : 'Expand'}
      </button>
    </>
  );
};

CMS.registerFieldType('notes', NotesControl);
```

```js [h()]
const { useState } = CMS.React;

const NotesControl = ({ forID, classNameWrapper, value, onChange }) => {
  const [expanded, setExpanded] = useState(false);

  return h(
    rf,
    null,
    h('textarea', {
      id: forID,
      className: classNameWrapper,
      rows: expanded ? 12 : 3,
      value: value ?? '',
      onChange: (event) => onChange(event.target.value),
    }),
    h(
      'button',
      { type: 'button', onClick: () => setExpanded(!expanded) },
      expanded ? 'Collapse' : 'Expand',
    ),
  );
};

CMS.registerFieldType('notes', NotesControl);
```

:::

A hook imported from another copy of React, such as the `react` package in your project’s dependencies, throws an “Invalid hook call” error. If you install the NPM package, import `React` from `@sveltia/cms` and take the hooks from it. If you load Sveltia CMS from the CDN, take the hooks from `CMS.React` (`window.CMS.React`), even when you bundle your own components with a build tool — importing `@sveltia/cms` in that case would load a second copy of the CMS. Never take them from the `react` package. The JSX itself can still be compiled with your project’s `react` package, as long as it’s React 19 too, so that Sveltia CMS can render the elements it creates.
