Loading episodes…
0:00 0:00

How to Auto-Compile Tailwind CSS in Jekyll With a Custom Plugin

00:00
BACK TO HOME

How to Auto-Compile Tailwind CSS in Jekyll With a Custom Plugin

10xTeam May 12, 2026 7 min read

If you use Tailwind CSS with Jekyll, you have probably seen this advice:

Add <script src="https://cdn.tailwindcss.com"></script> to your <head>.

It works instantly, which is great for prototyping. But it comes with a real cost in production: that CDN script downloads the entire Tailwind library — about 3 MB — on every page load, scans your HTML at runtime, and then applies the styles. Your visitors pay that cost every single visit.

The proper fix is to compile Tailwind ahead of time so only the CSS classes you actually use end up in the final file. A typical compiled output is around 10–100 KB — a 30× reduction.

The problem is that now you have two commands to run every time you want to build your site:

npx tailwindcss -i ./assets/css/tailwind.src.css -o ./assets/css/tailwind.css --minify
bundle exec jekyll build

Forget the first one and your site ships with stale CSS. This tutorial shows you how to make Jekyll run that command for you automatically.


What We’re Building

A small Jekyll plugin — 12 lines of Ruby — that hooks into Jekyll’s build lifecycle and compiles Tailwind before Jekyll processes any files. After this setup:

  • You run bundle exec jekyll build as normal
  • Tailwind compiles automatically first
  • You never think about it again

Prerequisites

Before starting, make sure you have:

  • A Jekyll site (any version)
  • Node.js installed (node -v should print a version number)
  • tailwindcss installed in your project:
npm install --save-dev tailwindcss

Step 1 — Create the Input CSS File

Tailwind needs a source file to start from. Create assets/css/tailwind.src.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

This is just three lines that tell Tailwind which parts of its library to include. You will never edit this file directly.


Step 2 — Create tailwind.config.js

Tailwind needs to know which files to scan for CSS classes. Create tailwind.config.js at the root of your project:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './_layouts/**/*.html',
    './_includes/**/*.html',
    './_posts/**/*.{html,md}',
    './*.html',
    './*.md',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

The content array tells Tailwind which files to scan. It reads every class name it finds (like text-lg, bg-blue-500, flex) and includes only those in the output. Everything else is stripped out — that is where the size reduction comes from.

If you have pages in subdirectories, add them here. For example:

'./tools/**/*.html',
'./magazine/**/*.html',

Step 3 — Create the Jekyll Plugin

Create the file _plugins/tailwind_compiler.rb:

Jekyll::Hooks.register :site, :after_reset do |site|
  src = File.join(site.source, 'assets', 'css', 'tailwind.src.css')
  out = File.join(site.source, 'assets', 'css', 'tailwind.css')
  tw  = File.join(site.source, 'node_modules', '.bin', 'tailwindcss')

  if File.exist?(tw) && File.exist?(src)
    Jekyll.logger.info 'TailwindCSS:', 'Compiling...'
    system("#{tw} -i #{src} -o #{out} --minify")
    Jekyll.logger.info 'TailwindCSS:', 'Done'
  else
    Jekyll.logger.warn 'TailwindCSS:', 'Skipping — binary or src not found'
  end
end

That’s it. Let’s walk through what each line does.

Jekyll::Hooks.register :site, :after_reset — Jekyll fires different events during a build. :after_reset runs right at the start, before Jekyll reads any files. That makes it the perfect moment to compile CSS so Jekyll can copy the freshly compiled file into _site/.

File.join(site.source, ...)site.source is the root folder of your Jekyll project. This builds the correct path to each file regardless of where Jekyll is running from.

if File.exist?(tw) && File.exist?(src) — A safety check. If node_modules is not installed or the source file is missing, the plugin logs a warning and skips instead of crashing the build. This means the plugin is safe to commit even for team members who have not run npm install yet.

system("#{tw} -i #{src} -o #{out} --minify") — Runs the Tailwind CLI with the --minify flag so the output is as small as possible.


Step 4 — Update Your Layout

Replace the CDN script tag in your layout’s <head>:

<!-- Before -->
<script src="https://cdn.tailwindcss.com"></script>

<!-- After -->
<link rel="stylesheet" href="/assets/css/tailwind.css">

Step 5 — Add the Output File to .gitignore

The compiled tailwind.css is a build artifact — it should be generated, not committed:

# .gitignore
assets/css/tailwind.css

If you are deploying to a platform that does not run npm install and jekyll build (for example, you just push static files), leave tailwind.css out of .gitignore and commit it instead. The plugin will still re-compile it on every local build.


Step 6 — Run It

bundle exec jekyll build

You will see Tailwind’s output in the build log:

TailwindCSS: Compiling...
TailwindCSS: Done
  Generating... done in 1.234 seconds.

The compiled assets/css/tailwind.css now exists and Jekyll copies it into _site/assets/css/tailwind.css as part of the normal build.


Why Not Just Use npm run build?

You can chain commands in package.json:

"build": "npx tailwindcss -i ... -o ... --minify && bundle exec jekyll build"

This works, but it means:

  • Everyone on the project has to remember to use npm run build instead of bundle exec jekyll build
  • CI/CD pipelines need to be configured to use the npm script
  • jekyll serve --livereload will not recompile Tailwind automatically

The plugin approach keeps everything inside Jekyll’s own build process. You use the same jekyll build command you always have.


Deployment

If you are deploying to Cloudflare Pages or Netlify, set the build command to:

npm install && bundle exec jekyll build

npm install puts the Tailwind binary in node_modules/. The plugin finds it there and compiles automatically during jekyll build.


Summary

Step What you created
1 assets/css/tailwind.src.css — Tailwind entry point
2 tailwind.config.js — tells Tailwind which files to scan
3 _plugins/tailwind_compiler.rb — auto-runs before every build
4 Updated <head> to use compiled CSS instead of CDN
5 Added compiled output to .gitignore

Your Jekyll site now ships with a CSS file that contains only the styles it actually uses, compiled automatically on every build — no extra commands, no manual steps.


Join the 10xdev Community

Subscribe and get 8+ free PDFs that contain detailed roadmaps with recommended learning periods for each programming language or field, along with links to free resources such as books, YouTube tutorials, and courses with certificates.

Audio Interrupted

We lost the audio stream. Retry with shorter sentences?