Recent searches

in

Listen for changes with the Visual Editor API

Last modified: August 11th, 2026

On this page

The Visual Editor API is designed for experienced CloudCannon developers. If you need help with custom integrations or your CloudCannon setup in general, please contact our friendly support team.

CloudCannon's Visual Editor API exposes change and delete events on the API root, on individual files, and on Collections and Datasets. Add listeners with addEventListener and remove them with removeEventListener when your custom UI or preview is torn down.

This article assumes you know how to initialize the Visual Editor API and access files, Collections, and Datasets. For a complete list of API objects and methods, please read our reference documentation on the Visual Editor API.

Event types and scopes#

You can listen to two types of events using the Visual Editor API: change, which fires when a file in your Site is created or updated, or delete, which fires when a file is deleted. Both use the standard addEventListener and removeEventListener interface from EventTarget.

Each event carries a detail object. Because change fires for both new and updated files, check event.detail.isNew to tell them apart. The value is true when the file was newly created and false when an existing file was updated. Every event also carries event.detail.sourcePath, the path of the file that changed, which is most useful on root, Collection, and Dataset listeners, where one listener covers many files.

You can also attach listeners at multiple levels, depending on how broad or narrow you need your events to be, including root, file, Collection, and Dataset.

Root level events

Calling *.addEventListener() on the API Object allows you to receive events for any change across your entire Site.

JavaScript
Copied to clipboard
const api = window.CloudCannonAPI.useVersion('v1', true);
api.addEventListener('change', (event) => { 
  console.log('Something changed');
  console.log(event.detail.sourcePath); 
  console.log(event.detail.isNew); 
});

Call api.addEventListener() with 'change' to listen for any create or update event across your entire Site.

event.detail.sourcePath is the path of the file that changed, relative to your Site root.

event.detail.isNew is true when the file was newly created, and false when an existing file was updated.

File level events

You can listen to a specific file object using *.addEventListener() to receive events for that file only.

JavaScript
Copied to clipboard
const file = api.currentFile(); 

file.addEventListener('change', () => { 
  console.log('Current file changed');
});

file.addEventListener('delete', () => { 
  console.log('Current file was deleted');
});

Create a file variable to access the file currently open in the Visual Editor using api.currentFile().

Call file.addEventListener() with 'change' to listen for create or update events on file.

Call file.addEventListener() with 'delete' to listen for delete events on file.

Collection and Dataset level

Calling *.addEventListener() on a Collection or Dataset allows you to receive events for any file within it.

For more information about how Collections and Datasets work with the Visual Editor API, please read our documentation on listing files, Collections, and Datasets with the Visual Editor API.

JavaScript
Copied to clipboard
const posts = api.collection('posts'); 

posts.addEventListener('change', (event) => { 
  console.log('A post was created or updated');
  console.log(event.detail.sourcePath, event.detail.isNew); 
});

posts.addEventListener('delete', () => { 
  console.log('A post was deleted');
});

Create a posts variable to access the posts Collection.

Call posts.addEventListener() with 'change' to listen for create or update events on any file in the Collection.

event.detail.sourcePath is the path of the file that changed, and event.detail.isNew is true for newly created files and false for updates. Dataset events carry the same detail fields.

Call posts.addEventListener() with 'delete' to listen for delete events on any file in the Collection.

JavaScript
Copied to clipboard
const locales = api.dataset('locales'); 

locales.addEventListener('change', () => { 
  console.log('A locale was created or updated');
});

locales.addEventListener('delete', () => { 
  console.log('A locale was deleted');
});

Create a locales variable to access the locales Dataset.

Call locales.addEventListener() with 'change' to listen for create or update events on any file in the Dataset.

Call locales.addEventListener() with 'delete' to listen for delete events on any file in the Dataset.

Update your custom integration after a change#

A common pattern is to re-render your custom integration whenever data changes. For worked examples of re-rendering on change and delete events, debouncing frequent updates, and reading the file that changed, please read our documentation on common patterns for the Visual Editor API.

Clean up listeners#

Remove event listeners when your custom integration is no longer active to avoid memory leaks and unexpected behavior. Always store your listener in a variable so you can pass the same reference to removeEventListener.

JavaScript
Copied to clipboard
const onChange = () => { /* ... */ }; 

api.addEventListener('change', onChange); 

window.addEventListener('beforeunload', () => { 
  api.removeEventListener('change', onChange);
});

Store your listener in a variable so you can reference it when removing it. Passing an inline arrow function to removeEventListener will not work, as it will not match the original function reference.

Register the listener on the API Object.

Remove the listener when the page is about to unload.

If you are building with a framework, clean up listeners in the appropriate lifecycle hook. The following examples use React and Vue, but the approach is the same in any framework: add the listener when the component mounts, and remove it when the component unmounts. Both examples use the withCloudCannonApi helper from initializing the Visual Editor API.

JavaScript
Copied to clipboard
useEffect(() => {
  let api; 
  const onChange = () => { /* ... */ };

  withCloudCannonApi((cloudCannon) => {
    api = cloudCannon;
    api.addEventListener('change', onChange);
  });

  return () => api?.removeEventListener('change', onChange); 
}, []);

Declare api and onChange in the effect so the cleanup function can reach them, then capture the API Object inside the helper callback.

Return a cleanup function from useEffect to remove the listener when the component unmounts. Returning it from inside the withCloudCannonApi callback instead would not work, as useEffect never receives it.

JavaScript
Copied to clipboard
let api;
const onChange = () => { /* ... */ };

onMounted(() => {
  withCloudCannonApi((cloudCannon) => {
    api = cloudCannon; 
    api.addEventListener('change', onChange);
  });
});

onUnmounted(() => {
  api?.removeEventListener('change', onChange); 
});

Declare api and onChange at setup scope so both lifecycle hooks can reach them, then capture the API Object here.

Call removeEventListener in onUnmounted to clean up the listener when the component is destroyed.

Related Resources

Open in a new tab