Skip to content

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 or webpack, which doesn’t load any files from CDNs. See CDN or NPM Package for how it differs from the CDN builds.

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

bash
npm install @sveltia/cms
bash
yarn add @sveltia/cms
bash
pnpm add @sveltia/cms
bash
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:

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, Custom Editor Components and Custom 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 — HTML-like markup in a tagged template literal. It works without a build step, so it’s the recommended way.
  • JSX — The familiar React syntax, which requires a build step.
  • 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 HTM ​

HTM lets you write JSX-like markup in a standard JavaScript tagged template literal, 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 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.

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 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 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:

js
const { useState, useEffect } = CMS.React;
js
import CMS, { React } from '@sveltia/cms';

const { useState, useEffect } = React;

For example, the following control for a custom field type keeps whether its text area is expanded in state:

js
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
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
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.