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 CloudCannon's Visual Editor API is a public JavaScript API for building custom integrations with the Visual Editor. It lets you run your own code inside the Visual Editor to read and write content, listen for changes, and upload files using code running inside your website files. A group of related files with a similar format (e.g., a folder of pages, blog posts, or data files). Once you group your files into Collections, they appear in the Site Navigation for easy access. A structured data file or folder defined under Visual Editor API
change and delete events on the API root, on individual files, and on CollectionsCollection
Dataset
data_config in your CloudCannon Configuration File. Datasets are used to store reusable data such as navigation links, locale strings, or site settings, and can be accessed and updated through CloudCannon's editing interfaces.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 An API object is an object that exposes an API's methods, so you can work with a service or system in code. In CloudCannon, the Visual Editor API provides an API Object as its entry point: call *.addEventListener() on the API ObjectAPI Object
useVersion() on window.CloudCannonAPI to get it, then use its methods to access files, Collections, and Datasets, read and write content, listen for changes, and build custom integrations in the Visual Editor.
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.
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.
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.
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.
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.
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.
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.