This is a recently introduced feature!
We're gradually introducing this change to our starters and examples.

What is Content Reload?

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

This is a core benefit of Stackbit: everything you or your 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.

How it worked previously

In projects using Stackbit version 0.4.0 Stackbit did not take part in that process.

Instead, we've provided the optional Sourcebit package along with plugins for various content sources and web frameworks. Using this package (or its more lightweight alternative), your website's code included both the code to detect changes on its server-side and trigger a refresh in all clients.

However, this approach requires installing packages and adding non-trivial code on both server- and client-side. So we've set out to make it easier: less obtrusive, easier to adapt to new frameworks, and more optimization-friendly. Here's the current approach.

Automatic Content Reload

Starting with Stackbit version 0.5.0, all this work is performed by Stackbit by default. 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 Stackbit 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, the 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.

Capturing & handling change events

In automatic mode, Stackbit 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.

Adding an event listener

To receive change events in your client-side code, add a window-level event listener with plain Javascript:

window.addEventListener('stackbitObjectsChanged', (event) => {
  /* ... */
})

On each change, your callback will be run.
The event argument has a detail property with the following contents:

{
  // Current page URL, e.g. '/' or '/about'
  currentUrl: string,
  // Current page object ID, based on the URL mapping in stackbit.yaml
  // e.g. 'src/pages/index.md',
  currentPageObjectId: string | null,
  // Changed objects, e.g. ['src/pages/index.md'] for Git-based content
  // or ['5YPPXqxAL5tJ6hdJu'] with a headless CMS
  changedObjectIds: string[],
  // Matching array of changed object types, e.g. ['PageLayout']
  changedContentTypes: string[],
  // All IDs found in data-sb-object-id annotations on this page,
  // e.g. ['src/pages/index.md', 'src/data/authors/jane.json']
  visibleObjectIds: string[]
}

To handle this event yourself and disable the default page refresh behavior, make sure to call event.preventDefault() in your callback code - just like you would with standard DOM events.

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') {
      e.preventDefault()
      /* 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 Stackbit'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) => e.detail.visibleObjectIds.includes(o))
  if (!intersects) e.preventDefault()
}

Custom Content Reload

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

# In stackbit.yaml with stackbitVersion >= 0.5.0:
customContentReload: true

With customContentReload set to true Stackbit 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 Stackbit 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.