JS files not updating, HBS files do

I’m not sure why but it seems all files with .hbs extension are updated every time I update my theme. But if any file with .js extensions don’t seem to be updated every time I upload a new version of theme.

Did anyone ever face a similar issue?

I found some bugs with the existing JS files in the template I recently bought hence I needed to change the JS - made the changes locally but when I uploaded it, it still maintain the older version of the JS file and doesn’t even change anything.

This however, doesn’t happen with any .hbs files. (e.g. default.hbs, post.hbs, index.hbs). It only happens with any JS files in the assets / built folder (e.g. index.js, mobileMenu.js, slider.js) Why is that?

For example, with WordPress, we can always append a versioning for any JS files we’re updating like some_wordpress_template.js?v=12352300412

but I wonder if Ghost has the same capability - this versioning could actually allow someone to update the JS files with different version etc.

Many themes need a build step, because they compile javascript and css. I’m guessing yours does, and you haven’t done that, which means the browser is loading exactly what it’s being told to load – the original version!

What theme is this? Check the theme developer’s documentation?

Thanks for the revert! I’m using Spotlight: Spotlight

I used to compile a build with Django and React custom code before but how do you compile a template build in Ghost?

Hmm. I don’t see it in their documentation. You may need to contact them, but maybe we can figure it out real quick.

Does the theme folder (after unzipping) have a package.json file? Take a look at what’s inside - does it define a process for building the theme?

Is there a gulpfile.js in the theme folder?

Yes, there is a package.json.

The content of the JSON looks like this:

  "name": "spotlight",
  "description": "Theme boasting stunning design and ultimate functionality for effortlessly elevating your website to new heights.",
  "demo": "https://spotlight-primary.highfivethemes.com",
  "version": "1.1.6",
  "engines": {
    "ghost": ">=5.0.0"
  "license": "MIT",
  "screenshots": {
    "desktop": "assets/screenshot-desktop.jpg",
    "mobile": "assets/screenshot-mobile.jpg"
  "scripts": {
    "dev": "rollup -c --environment BUILD:development -w",
    "build": "rollup -c --environment BUILD:production",
    "zip": "npm run build && bestzip $npm_package_name.zip assets/* partials/* members/* *.hbs package.json rollup.config.js",
    "test": "npx gscan .",
    "pretest": "npm run build"
  "author": {
    "name": "HighFiveThemes",
    "email": "hello@highfivethemes.com",
    "url": "https://spotlight.highfivethemes.com"
  "gpm": {
    "type": "theme",
    "categories": [
  "keywords": [
  "devDependencies": {
    "@babel/core": "^7.21.0",
    "@babel/preset-env": "^7.20.2",
    "@rollup/plugin-babel": "^6.0.3",
    "@rollup/plugin-commonjs": "^24.0.1",
    "@rollup/plugin-node-resolve": "^15.0.1",
    "@rollup/plugin-terser": "^0.4.0",
    "bestzip": "^2.2.1",
    "postcss-import": "^15.1.0",
    "postcss-preset-env": "^8.0.1",
    "rollup": "^3.18.0",
    "rollup-plugin-livereload": "^2.0.5",
    "rollup-plugin-postcss": "^4.0.2"
  "browserslist": [
  "config": {
    "card_assets": true,
    "posts_per_page": 8,
    "image_sizes": {
      "112": {
        "width": 112
      "200": {
        "width": 200
      "300": {
        "width": 300
      "500": {
        "width": 500
      "600": {
        "width": 600
      "800": {
        "width": 800
      "1000": {
        "width": 1000
      "1200": {
        "width": 1200
      "1600": {
        "width": 1600
    "custom": {
      "navigation_layout": {
        "type": "select",
        "options": [
          "Logo on the left",
          "Author on the left",
          "Logo in the middle",
          "Author in the middle",
          "Logo with social icons",
          "Author with social icons",
          "Mobile menu with logo",
          "Mobile menu with author",
          "Mobile menu with logo and icons",
          "Mobile menu with author and icons"
        "default": "Logo on the left"
      "dark_mode_logo": {
        "type": "image"
      "about_primary": {
        "type": "text",
        "default": "Unveiling the Secrets: Exploring the Wonders of the World",
        "group": "homepage"
      "about_secondary": {
        "type": "text",
        "default": "In this captivating blog, embark on a journey to unravel the mysteries and marvels that our world has to offer.",
        "group": "homepage"
      "hero_section_layout": {
        "type": "select",
        "options": [
          "Only text",
          "Image on the right",
          "Slider on the right",
          "Bottom slider",
          "Featured posts",
          "Without hero section"
        "default": "Image on the right",
        "group": "homepage"
      "enter_tag_slugs_for_big_carousel": {
        "type": "text",
        "group": "homepage"
      "enter_tag_slugs_for_small_carousel": {
        "type": "text",
        "group": "homepage"
      "enter_tag_slugs_for_big_grid": {
        "type": "text",
        "group": "homepage"
      "enter_tag_slugs_for_small_grid": {
        "type": "text",
        "group": "homepage"
      "cta_text_for_subscription_section": {
        "type": "text",
        "default": "Subscribe to New Posts"
      "cta_subtext_for_subscription_section": {
        "type": "text",
        "default": "Lorem ultrices malesuada sapien amet pulvinar quis. Feugiat etiam ullamcorper pharetra vitae nibh enim vel."
      "button_color": {
        "type": "color",
        "default": "#000000"
      "background_color": {
        "type": "color",
        "default": "#ffffff"
      "border_radius": {
        "type": "select",
        "options": [
          "Slightly rounded",
        "default": "Rounded"
      "typography": {
        "type": "select",
        "options": [
        "default": "Sans-serif"
  "type": "module"

The thing is I performed some code changes on their file called assets/js/index.js where I’m revising a few lines in their code to resolve couple of bugs existing on their template. Their template is nice but really buggy.

And then I ran npm run build so that the new build is recompiled and then I uploaded this version on Ghost.

The only issue I’m having is it’s not updating the index.js file hosted in Ghost domain. It’s still showing the old version.

So in another word, JS files are updated locally - but just not in production when I upload the theme via Ghost dashboard. HBS files are updated, but not the JS files.

OK, so you’re running npm run zip also, to generate the zip file, which you’re uploading through the admin panel?

Or did you set up the github deploy action, in which case you’re npm run build and then committing the changes. If you’re using the github deploy, check that your .gitignore isn’t excluding the assets folder, nor the dist folder if this theme has one.

yes, i ran the npm run zip as well. and upload it via Design & Branding > Upload section.

Do you guys have some sort of server cache going on? Where some files (in this case, it’s only JS files in particular) uploaded by any user to your domain is cached/stored permanently? Cuz I tried another experiment by deleting some JS files in the build - and it seems that the JS files that deleted locally still remain in Ghost domain.

So sometimes, when I can’t delete a JS file, I had to create another JS file with a different name so that it’s uploaded to Ghost - and it actually works when the JS file is uploaded the first time to Ghost domain. But then, when you try to modify / delete the previously uploaded JS file, that’s where the problem comes.

For example, let’s say the original file I want to replace is assets/js/index.js however, this file somehow can’t be updated/unable to be deleted.

Hence, the route that I ended up doing is to create another file called index_03272024.js and only then this file is successfully uploaded to Ghost domain since it’s the first time the file is being introduced to the build.

How are you loading the JS file in the theme itself?

In the default.hbs:
<script src="{{asset 'built/index.js'}}" ></script>

Then, in the assets/index.js, this is how the template is designed to load the JS dependencies required to load all the theme features:

// Import CSS

    // Import JS
    const { toggleDarkMode } = require('./darkMode');
    const { initSliders } = require('./initSliders');
    const { loadMore } = require('./loadMore');
    const { copyToClipboard } = require('./copyToClipBoard');
    const { changeFontColor, changeFontColorOnToggle } = require('./footerFontColor');
    const { hideHeader, showSubNav } = require('./header');
    const { initMobileMenu } = require('./mobileMenu');
    const { togglePlan } = require('./membershipToggle');
    const { scrollTop } = require('./scrollTop');
    const { centerSwiperBtns } = require('./centerSliderBtns');
    const { calcHeight } = require('./membershipCard');
    const { scrollAnimate } = require('./scrollAnimate');
    const { scrollTopAlign } = require('./scrollTop');
    const { changeTwitterCardTheme } = require('./twitterTheme');


    const toggleDMBtns = document.querySelectorAll('.gh-dark-mode-toggle-btn');
    toggleDMBtns.forEach((btn) => {
      btn.addEventListener('click', changeFontColorOnToggle);
    window.addEventListener('load', changeTwitterCardTheme);
    window.addEventListener('resize', centerSwiperBtns);
    window.addEventListener('load', scrollTopAlign);

Thanks for sharing that.

Ghost uses the asset helper to bust the cache, so you should be all good there. How are you hosting?

I didn’t reintroduce anything new logic to load the JS as the above has been preset by the theme just the way I bought it.

But there were some issues with their JS dependencies (which are a lot) in the index.js not fully loading before DOMContentLoaded - hence I was trying to optimize that by adding a few lines.

Unfortunately, no matter how many fixes I’m introducing to the JS locally, it won’t be uploaded to Ghost domain.

Ah I see. Now that makes sense - that’s exactly the feature what I was looking for @ryanF. Right now we’re only doing DNS pointing while still having our files hosted in (businessName).ghost.io - is the feature available for this type of hosting?

p.s. By the way how do I perform cache busting to the existing assets? Can you guys do that from your end? Or should I add ?v= to every dependency URL?

I was asking about hosting because I thought it might’ve been caching at the hosting level.

The asset helper busts the cache for you. You don’t need to do anything. How are you uploading your theme?

Also, because you’re on Ghost(Pro), you can reach out to support, as they can provide direct support (support@ghost.org).