swup swup Reloading Scripts
GitHub swup on GitHub

Reloading Scripts

Swup takes control of the page load lifecycle. Instead of having the browser recompile scripts between page changes, it keeps the current page instance alive and only updates the content containers to match the content of the new page.

This means two things:

  • You can not rely on standard browser events to trigger your custom code
  • You have to pay attention to not leaking memory by undoing any changes

Triggering custom code

Swup updates pages without a full reload, so you can't rely on DOMContentLoaded or other events to trigger your code as they will only ever run once on initial load.

document.addEventListener('DOMContentLoaded', () => {
  // This only runs once, so we need some other event for triggering code
});
document.addEventListener('DOMContentLoaded', () => {
  // This only runs once, so we need some other event for triggering code
});

Instead, we can trigger code after each page change by registering handlers for swup's hooks. Combining the browser event and swup hooks, we end up with a template for reliably running code to initialize elements on the page:

document.addEventListener('DOMContentLoaded', () => {
  // This runs on initial load
});

swup.hooks.on('page:view', () => {
  // This runs after every page change by swup
});
document.addEventListener('DOMContentLoaded', () => {
  // This runs on initial load
});

swup.hooks.on('page:view', () => {
  // This runs after every page change by swup
});

Initializing components

We'll collect our scripts in one init function that we can call repeatedly to initialize everything. Each script has a condition to make sure it's only run when a related element is found on the page.

function init() {
  if (document.querySelector('#carousel')) {
    // new Carousel('#carousel')
  }

  if (document.querySelector('#lightbox')) {
    // $('#lightbox').lightbox()
  }

  if (document.querySelector('#something-else')) {
    // and so on
  }
}
function init() {
  if (document.querySelector('#carousel')) {
    // new Carousel('#carousel')
  }

  if (document.querySelector('#lightbox')) {
    // $('#lightbox').lightbox()
  }

  if (document.querySelector('#something-else')) {
    // and so on
  }
}

Then we register the correct event handlers and we're good to go.

const swup = new Swup();

// Run once when page loads
if (document.readyState === 'complete') {
  init();
} else {
  document.addEventListener('DOMContentLoaded', () => init());
}

// Run after every additional navigation by swup
swup.hooks.on('page:view', () => init());
const swup = new Swup();

// Run once when page loads
if (document.readyState === 'complete') {
  init();
} else {
  document.addEventListener('DOMContentLoaded', () => init());
}

// Run after every additional navigation by swup
swup.hooks.on('page:view', () => init());

Using component frameworks

While the above example is a perfectly fine solution to achieve code execution on page change, we suggest looking into JS frameworks that can help you automate some of this by mounting and unmounting components automatically and providing lifecycle hooks.

Avoiding memory leaks

Swup keeps a persistent session in memory, so objects leaking memory will not be cleaned up automatically as they would be on a full page refresh. While this should not be a problem on most sites, be aware that in special cases you will need to clean up after yourself right before swup's content:replace hook.

function unload() {
  if (document.querySelector('#carousel')) {
    // carousel.destroy()
  }
}

swup.hooks.before('content:replace', () => unload());
function unload() {
  if (document.querySelector('#carousel')) {
    // carousel.destroy()
  }
}

swup.hooks.before('content:replace', () => unload());

Third-party script tags

If you're not in control over the scripts included on the page, there is an official scripts plugin to help with reloading script tags. This should be a last resort if none of the other options are available to you.