How to trigger a resource re-build on every change/server reload in development?

Since Tailwind v3.0.11+, Hugo Pipes work perfectly to build CSS and no longer error out because of JIT mode. This was mentioned here by @praveenjuge as well as me. So, that solves the issue for production builds.

However, Hugo doesn’t trigger a CSS rebuild on a template change and simply rebuilds only the changed template/file (which is the desired behaviour with most tools). But since Tailwind v3+ pretty much requires a rebuild on every template change, this becomes a problem in development.

I tried disabling file caches, and the --disableFastRender flag, but that doesn’t do the trick.

Is there any way to trigger a (specific) resource re-build with every single file change/server reload instead of using a cached copy?

This would make the workflow much cleaner by reducing the need for npm scripts and/or other tools/steps to build the CSS in development. It would reduce the toolchain’s update latency as well.

2 Likes

Rather than Hugo triggering a Tailwind build, Tailwind should be watching for changes in your Hugo templates. This is how TW’s JIT compiler is designed to work. Just ensure that the content key in your Tailwind config points to your Hugo layouts and partials.

1 Like

@nternetinspired I understand. But that’s not how Hugo Pipes work. Hugo doesn’t rely on postcss-cli or Tailwind’s CLI to watch for changes but instead watches for changes itself and triggers a one-time build if a change is detected in concerned files. This worked great with AOT design but not with JIT since the CSS needs to be rebuilt on almost every template change without the source CSS file being ever touched at all.

Sure, running a separate process to watch for changes and leaving only the production build up to Hugo works. But having an option to trigger a build no matter what rather than caching it, would make the workflow much cleaner.

As for the performance hit, it wouldn’t be any greater than running a separate process for PostCSS build and triggering a Hugo rebuild 2 times – once for the template change and the other for CSS.

I configured it like this:

  • hugo server and tailwind jit run parallel to each other
  • tailwind watches tailwind files and recreates a compiled version of the styles in assets/theme.css
  • hugo watches assets/theme.css and compiles the stylesheet from that

For production tailwind runs and creates its stuff, then hugo runs and takes what it gets.

I don’t think Hugo needs to do the job of Tailwind in this case, because as far as my limited understanding goes the whole JIT part of Tailwind is only to minimise assets while developing. If you use a tool like npm run-p you configure the whole setup once and can forget it afterwards and it all goes up with an npm run and down with a CTRL+C.

1 Like

hey @davidsneighbour.

Like I mentioned, running separate processes works. I just wanted to find out if there’s an actual way to handle it through Hugo in a cleaner way.

In fact, I have a perfectly working setup right now:

  • My head template changes the source stylesheet according to the environment the site is in
    • in production, I have normal Hugo Pipes setup, which works perfectly since Tailwind v3.0.11+
    • in development, I use postcss-cli coupled with nodemon to trigger a build after a template change
      • I use nodemon because the --watch option with postcss-cli only watches for changes in the input file
      • and I use postcss-cli instead of Tailwind’s CLI because I also use the postcss-import plugin
  • I use npm-run-all or concurrently to run the hugo server and nodemon in parallel and start with a single command

This setup works great. However, this adds unnecessary steps, tools and time to the entire workflow.

Problems with the setup:

  • It triggers 2 Hugo builds (and thus a browser refresh) for every change which isn’t a very great dev experience
    • 1st when the template is saved
    • 2nd when PostCSS is finished processing
  • Even though the actual CSS build is finished in around 100-200 milliseconds, it adds almost a second or two to the workflow
    • it takes time because nodemon after detecting change invokes npm run as a separate instance each time

Earlier, Hugo Pipes couldn’t work with JIT at all. So this setup made a lot of sense. But now that it does, if there’s a possibility to trigger a resource build for every without caching it, it would be great. At least until, Hugo devs come up with an elegant solution.

Since Tailwind 3 the concept of dev vs production builds is largely removed IMO, as the built css contains only styles that are in use in your templates. As Tailwind JIT watches your templates I don’t personally see any benefit to having Hugo then also watch your built css! As such I’ve dropped Hugo pipes altogether and let PostCSS handle everything, given that it’s already a requirement of Tailwind. As such I can easily port my Tailwind setup to multiple platforms, with no changes required. FWIW, I mainly build sites with Craft but also use Jekyll or Hugo if they’re a better fit.

The way I have it setup is for production build to build sequentially, Hugo then Tailwind, and in development in paralell using GitHub - mysticatea/npm-run-all: A CLI tool to run multiple npm-scripts in parallel or sequential..

You can use post-css import directly with Tailwind, without any additional deps.

My Postcss config:

module.exports = {
  plugins: [
    require('postcss-import'),
    require('tailwindcss/nesting')(require('postcss-nesting')),
    require('tailwindcss'),
    require('postcss-preset-env')({
      features: { 'nesting-rules': false }
    })
  ]
}

…and Tailwind config

const colors = require('tailwindcss/colors')
const defaultTheme = require('tailwindcss/defaultTheme');

module.exports = {
  darkMode: 'media',
  content: [
    './src/js/**/*.*',
    './archetypes/**/*.*',
    './data/**/*.*',
    './layouts/**/*.html',
    './static/**/*.*',
  ],
  theme: {
    extend: {
      fontFamily: {
        sans: [
          'Inter',
          ...defaultTheme.fontFamily.sans,
        ]
      },
    },
  },
  variants: {
    extend: {
      fontSmoothing: ['dark'],
    }
  }
}

Hi @nternetinspired.

Hugo already watches all your files for changes, builds resources (CSS/JS), and reloads the server on-change in one-go. Except, it only rebuilds the changed file(s) and uses rest from the cache. So, I just want to know if there’s a way to alter that behaviour and invalidate a certain file and/or a resource pipe with every reload/rebuild in development (production already works fine).

Besides, JIT does not necessarily watch your files all the time. You can (it’s actually default behaviour) trigger a one-time build with JIT, where it scans your files for just that one time. This is what Hugo Pipes take benefit of.

You are right. Except, my prod/dev setup is not about CSS or Tailwind optimizations but making deployment/development workflow easy and clean. Preferably organizing everything under a single tool/command, which Hugo already does.

I am not willing to do that. I like just running hugo and getting everything ready for me rather than me setting up a npm script and using postcss-cli and feeding the built CSS manually/externally (or automated with nodemon) on every change to Hugo builds.

I know, but Tailwind’s CLI doesn’t use your postcss config. I tried it. You need postcss-cli to use any PostCSS plugin other than Tailwind itself.

It works for me as well. But I have noted some problems with this setup in my previous comment.

Thanks for trying to help out! I am just not looking to make it work the Hugo way.

1 Like

@cryptic-code – did you ever get this working more like what you wanted?

I haven’t needed such a setup since I posted this the first time. @klm

However, @Gioni06’s and @nnooney’s proposal to add a timestamp, a hash or a random string to the CSS filename as a workaround seems to be the obvious way to make this work the Hugo way.

Hey @cryptic-code , check out this article, it does exactly what you need Making Tailwind JIT work with Hugo, the Version 3 edition | BryceWray.com

There is a downside, though - the resources folder is polluted now with the useless junk and cannot be checked in to Git anymore. It is in Git for me because image resizing is a CPU-costly operation, and I prefer to store the computed results in Git to avoid recalculation.

1 Like