Visual editing /Visual Editor /

Automatic content reload

Learn how automatic content reload works and how you can customize it.

# Overview

Whenever a content editor makes a change - e.g. edit a field or add a component, everyone who's currently working on the same Visual Editor project should immediately see this change.

This is a core benefit of Visual Editor: everything you or your project collaborators do is visually reflected to all editors, instead of having to go through a lengthy server restart or publishing process just to preview changes.

Normally, sites running in production don't have this functionality: pages are either statically-generated as possible or otherwise heavily cached, and generally do not update on any content change after they've already been rendered in a user's browser.

To provide instant content updates in our Visual Editor, a few things need to happen:

  1. The web server should run in a mode that doesn't apply pre-generation or caching to pages. This is commonly known as development mode in most web frameworks.
  2. Content changes should be detected via some watching mechanism.
  3. When a change is detected, clients should be notified and refresh the page content without reloading the whole page, with minimal interruption to the end-user.
    This typically requires framework-specific code, e.g. for Next.js, using the router object to re-navigate to the current page.

This process is exactly the same for both the editor who just made the change and for any other users working concurrently. There is no separate code path for refreshing the page based on whether you've made the change or anyone else.

# Automatic content reload

Starting in version 0.5.0, here's how it works:

  1. When running locally, the stackbit dev process watches for changes in the content source configured for your site. When we run your site ourselves for content editors (which we do for every Visual Editor project), our runtime environment does the same.
  2. When a change is detected, all open Visual Editors get notified.
  3. At this point, your code can be notified and take control of what happens next - if you wish. We'll get to that in a second.
  4. Unless you've explicitly handled the change event in your codebase, Visual Editor will proceed to refresh the page by itself. The actual mechanism depends on the framework used (e.g. using the router object for Next.js, as mentioned above).

Hence, by default, everything just works - with no extra code required on your site.

# Capture & handle change events

In automatic mode, Visual Editor always detects content changes by itself. However, there are scenarios in which you may opt to handle the actual page refresh yourself:

  1. When there is a method to efficiently refresh the page that's specific to your site (e.g. calling an API endpoint you control that will bring in updated content for the page in the fastest manner). Check back soon for examples.
  2. When you want to bring in a web framework we don't yet have built-in support for and use its fast refresh path to update the page.
  3. If you want to control whether a displayed page will refresh at all, given which content objects have been changed.
    By default, a page refresh will always occur regardless of what content has changed, since any page may render multiple content objects (that are referenced by each other, etc.). However, we provide detailed data on what content has changed that you can use to make the call yourself. See event.detail below.

# Add an event listener

To receive change events in your client-side code, add a window-level event listener for the stackbitObjectsChanged event:

window.addEventListener("stackbitObjectsChanged", event => {
  // Override default refresh behavior ...

See the event reference for more details on the event object.

# Next.js example

The event listener will run on all pages by using the App component.

// src/pages/_app.js
import * as React from "react";

const CHANGE_EVENT = "stackbitObjectsChanged";

export default function MyApp({ Component, pageProps }) {
  const onContentChange = e => {
    // Override the default refresh behavior just for a specific URL
    if (e.detail.currentUrl === "/about") {
      /* myRefreshCodeJustForAbout() ... */

  When any page is mounted, add the listener.
  Note the function returned by the callback, which will be called on unmount,
  and the empty dependency array to prevent this from running on any re-render.
  React.useEffect(() => {
    window.addEventListener(CHANGE_EVENT, onContentChange);
    return () => {
      window.removeEventListener(CHANGE_EVENT, onContentChange);
  }, []);

  return <Component {...pageProps} />;

# Conditional refresh

You can override Visual Editor’s default behavior just in certain conditions or for specific URLs, by conditionally calling e.preventDefault() just when appropriate.

For example, if you're methodically annotating all content objects used to render a given page with data-sb-object-id data attributes, you can prevent a refresh when there's no intersection between changed objects and on-page objects:

const onContentChange = e => {
  const intersects = e.detail.changedObjectIds.some(o =>
  if (!intersects) e.preventDefault();

# Custom content reload

To handle content change detection & refresh fully within your codebase, add the following to your configuration file:

export default {
  stackbitVersion: "~0.6.0",
  customContentReload: true

With customContentReload set to true Visual Editor will not notify you of content changes, nor will it refresh the page.

# When to go fully custom

  • If you're using Sourcebit as many existing projects do.
    This package is optional and external to Visual Editor itself. It handles change detection & page refresh by itself for supported content sources & web frameworks.
  • If your application implements a cache layer on top of your content source, you need to ensure that this cache is up-to-date with content changes before the page is being refreshed. This requires having your own pipeline for content change detection => cache update/invalidation => page refresh. You can find an example of that in the sourcebit-target-next plugin.