This guide shows how to integrate Tailwind CSS into Hugo using the official css.TailwindCSS function.
We’ll start from an empty Hugo project, add a simple theme layout, wire up Tailwind, and run a dev server.
1. Create a new Hugo project
First, create an empty Hugo project skeleton. Here is an example using Docker:
docker run --rm -it \
-v "$(pwd):/project" \
-u $(id -u):$(id -g) \
ghcr.io/gohugoio/hugo:v0.157.0 new project . --force
After this command finishes, you should have a basic Hugo site in the current directory.
2. Set up the base layout
Now that we have our project structure in place, let’s create the basic theme layout files. First, create a
layouts/baseof.html file which will serve as our main template:
<!DOCTYPE html>
<html lang="{{ site.Language.LanguageCode }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
<head>
{{ partial "head.html" . }}
</head>
<body>
<header>
{{ partial "header.html" . }}
</header>
<main>
{{ block "main" . }}{{ end }}
</main>
<footer>
{{ partial "footer.html" . }}
</footer>
</body>
</html>
Now, let’s create these three partial templates. First, create layouts/partials/head.html:
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
{{ with (templates.Defer (dict "key" "global")) }}
{{ partial "head/css.html" . }}
{{ end }}
{{ partialCached "head/js.html" . }}
Let’s create header.html in the layouts/partials directory:
<h1>{{ site.Title }}</h1>
And footer.html in the same directory:
<p>Copyright {{ now.Year }}. All rights reserved.</p>
Now let’s add two template files that handle our CSS and JavaScript assets:
layouts/partials/head/css.html:
<!-- CSS will be wired here in the Tailwind section -->
layouts/partials/head/js.html:
{{- with resources.Get "js/main.js" }}
{{- $opts := dict
"minify" (not hugo.IsDevelopment)
"sourceMap" (cond hugo.IsDevelopment "external" "")
"targetPath" "js/main.js"
}}
{{- with . | js.Build $opts }}
{{- if hugo.IsDevelopment }}
<script src="{{ .RelPermalink }}"></script>
{{- else }}
{{- with . | fingerprint }}
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
{{- end }}
{{- end }}
{{- end }}
{{- end }}
Finally, create a simple home template at layouts/home.html:
{{ define "main" }}
<h2>{{ .Title }}</h2>
{{ .Content }}
{{ end }}
If you don’t use taxonomies (tags, categories), you can disable them to avoid having to create templates for them.
In hugo.toml add:
disableKinds = ['taxonomy', 'term']
At this point the site renders basic HTML, and we’re ready to integrate Tailwind.
3. Configure Hugo for Tailwind
We’ll use Hugo’s css.TailwindCSS function, which relies on hugo_stats.json to know which classes are used.
Add the following configuration to hugo.toml:
[build]
[build.buildStats]
enable = true
[[build.cachebusters]]
source = 'assets/notwatching/hugo_stats\.json'
target = 'css'
[[build.cachebusters]]
source = '(postcss|tailwind)\.config\.js'
target = 'css'
[module]
[[module.mounts]]
source = 'assets'
target = 'assets'
[[module.mounts]]
disableWatch = true
source = 'hugo_stats.json'
target = 'assets/notwatching/hugo_stats.json'
This tells Hugo to generate hugo_stats.json and make it available to the Tailwind setup.
4. Install Tailwind CSS
We’ll be using the official Hugo function css.TailwindCSS. So let’s install the necessary dependencies:
npm install --save-dev tailwindcss @tailwindcss/cli @tailwindcss/typography
or using Docker:
docker container run --rm -it \
-v "$PWD:/usr/src/app" \
-w /usr/src/app \
-u $(id -u):$(id -g) \
node:22-alpine npm install --save-dev tailwindcss @tailwindcss/cli @tailwindcss/typography
Now that we have the necessary dependencies installed, let’s wire up Tailwind.
Create a CSS entry file at assets/css/main.css:
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@source "hugo_stats.json";
Put the template for CSS in layouts/partials/head/css.html:
{{ with resources.Get "css/main.css" }}
{{ $opts := dict "minify" (not hugo.IsDevelopment) }}
{{ with . | css.TailwindCSS $opts }}
{{ if hugo.IsDevelopment }}
<link rel="stylesheet" href="{{ .RelPermalink }}">
{{ else }}
{{ with . | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{ end }}
{{ end }}
{{ end }}
{{ end }}
That’s it! Now we can start adding Tailwind CSS classes to our HTML templates.
Testing
Add content to content/_index.md:
docker run --rm -it \
-v "$(pwd):/project" \
-u $(id -u):$(id -g) \
ghcr.io/gohugoio/hugo:v0.157.0 new content _index.md
Run hugo server and open http://localhost:1313 in your browser.
docker run --rm -it \
-v "$(pwd):/project" \
-u $(id -u):$(id -g) \
-p 1313:1313 \
ghcr.io/gohugoio/hugo:v0.157.0 server --bind=0.0.0.0
Tailwind config
If you want to use Tailwind config, create file tailwind.config.js in the project root:
module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [],
};
Then add the next rule to the main.css:
@config "./tailwind.config.js";
Andrew Dorokhov