---
url: /en/docs/api/events.md
description: >-
  Use event hooks in Sveltia CMS to execute custom code in response to CMS
  events.
---

# Event Hooks

Event hooks allow developers to execute custom code in response to specific events within Sveltia CMS. This feature enables advanced customization and integration with other systems by providing a way to listen for and react to various actions taken within the CMS.

## Overview

To register an event listener, use the `registerEventListener` method on the [`CMS` object](/en/docs/api#accessing-the-cms-object):

```js
CMS.registerEventListener({ name, handler });
```

The `registerEventListener` method allows you to register a callback function (`handler`) that will be invoked when a specific event (`name`) occurs within the CMS. The handler function receives an object containing relevant data about the event, allowing you to perform custom logic based on the event context.

Multiple event listeners can be registered for the same event, and they will be executed in the order they were registered.

### Parameters

* `name` (string): The name of the event to listen for. See the [Supported Events](#supported-events) section for a list of available events.
* `handler` (function): A callback function that will be executed when the event is triggered. See the [Event Handler](#event-handler) section for details on the parameters passed to the handler.

## Supported Events

The following events are supported for event hooks:

* `preSave`: Triggered before an entry is saved. You can modify the entry data before it is persisted.
* `postSave`: Triggered after an entry has been saved.

Additionally, the following events are available when using [Editorial Workflow](/en/docs/workflows/editorial):

* `prePublish`: Triggered before an entry is published. Unlike `preSave`, the handler can’t modify the entry: the content is already committed to the pull/merge request by this point, so a returned value is ignored.
* `postPublish`: Triggered after an entry has been published.
* `preUnpublish`: Triggered before a published entry is removed from the configured branch, which happens when a deletion is published rather than when it’s requested.
* `postUnpublish`: Triggered after a published entry has been removed from the configured branch.

## Event Handler

The handler function receives an object with the following properties:

* `author`: The author object that contains the `login` (login name) and `name` (display name) of the user who triggered the event. Both are always strings: a value that isn’t available, such as with the [local development workflow](/en/docs/workflows/local), which doesn’t track user information, is an empty string.
* `entry`: The entry object serialized to an [Immutable Map](https://immutable-js.com/docs/v5/Map/). It contains the following properties:
  ```js
  {
    data: { ... }, // Default locale data
    i18n: {
      [locale]: {
        data: { ... } // Non-default locale data
      }
    },
    slug, // Entry slug
    path, // Entry path
    newRecord, // Boolean indicating if it's a new entry
    collection, // Collection name, or `_singletons` for a singleton
    mediaFiles, // Array of associated media files
  }
  ```

For the `preSave` event, the handler can return a modified entry object in Immutable Map format, or just the modified `data` Map like `entry.get('data').set('title', 'New Title')`, to change the data before it is saved. Only the changes to `data` and `i18n.*.data` are applied; changes to other properties, such as `slug`, are ignored. The handler can be asynchronous and return a Promise that resolves to the modified `entry` or entry `data`. If multiple handlers are registered, each one receives the changes made by the previous ones.

For other events, the return value is ignored.

## Examples

### Modifying Entry Data Before Save

The following example demonstrates how to register a pre-save hook that adds a last modified timestamp to the entry data before it is saved.

```js
CMS.registerEventListener({
  name: 'preSave',
  handler: ({ entry }) => {
    return entry.get('data').set('last_modified', new Date().toISOString());
  },
});
```

### Accessing I18n Data

If you have [internationalization](/en/docs/i18n) (i18n) support enabled, localized data can be accessed and modified within the event handlers, under the `i18n` property of the entry object. The following example shows how to read and update localized fields in a pre-save hook, assuming the entry has English (default), French and other locales configured.

```js
CMS.registerEventListener({
  name: 'preSave',
  handler: ({ entry }) => {
    console.info('English Title:', entry.getIn(['data', 'title']));

    entry.get('i18n').forEach((localeData, locale) => {
      console.info(`Locale (${locale}) Title:`, localeData.getIn(['data', 'title']));
    });

    return entry.setIn(['i18n', 'fr', 'data', 'title'], 'Titre en Français');
  },
});
```

The [`getIn`](https://immutable-js.com/docs/v5/Map/#getIn\(\)) and [`setIn`](https://immutable-js.com/docs/v5/Map/#setIn\(\)) methods from Immutable.js are used to work with nested data structures.

### Accessing Media Files

The `mediaFiles` property of the entry object lists the assets referenced by the entry’s [Image](/en/docs/fields/image) and [File](/en/docs/fields/file) fields that are stored in a [collection-level or field-level media folder](/en/docs/media/internal). Assets in the global media folder aren’t included. Each item has the following properties:

* `id`: The Git object ID (SHA-1 hash) of the file.
* `name`: The file name.
* `path`: The file path, relative to the repository root.
* `size`: The file size in bytes.
* `url` and `displayURL`: A temporary [blob URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static) for the file, or `undefined` if it hasn’t been loaded into the browser yet.
* `file`: The [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) object. It’s only set for newly uploaded files that haven’t been saved yet, so you can inspect what’s about to be committed.

The following example demonstrates how to register a pre-save hook that lists the media files associated with the entry and warns about large uploads.

```js
CMS.registerEventListener({
  name: 'preSave',
  handler: ({ entry }) => {
    entry.get('mediaFiles').forEach((media) => {
      const { name, path, size, file } = media.toJS();

      console.info(`${file ? 'Uploading' : 'Referencing'} ${name} (${size} bytes) at ${path}`);

      if (file && size > 1024 * 1024) {
        console.warn(`${name} is larger than 1 MB`);
      }
    });
  },
});
```

The handler doesn’t need to return anything here because the entry data isn’t modified.

### Getting Notification of Saved Entries

The following example demonstrates how to register a post-save hook that logs information about the saved entry and the author who made the changes.

```js
CMS.registerEventListener({
  name: 'postSave',
  handler: ({ author, entry }) => {
    console.log(`Entry saved by ${author.login || 'Unknown'}:`, entry.toJS());
  },
});
```

The [`toJS`](https://immutable-js.com/docs/v5/Map/#toJS\(\)) method from Immutable.js is used to convert the entry object back to a plain JavaScript object for easier logging.

## Showcase

Real-world examples of event hooks can be found in our [showcase](/en/showcase?feature=events).
