Alpine.js and Tailwind HTML setup

Sat, Mar 12, 2022 4-minute read

Plain HTML with Alpine and Tailwind CSS

Today I set up a Go project using Tailwind and Alpine.js. This web application uses Go templates so there was no need for any React or other javascript frontend. I did want dropdowns and to use tailwind without a CDN. This meant I needed to install some NPM packages and include some javascript to facilitate the dropdowns.

This is the minimum requirement to get a basic Tailwind and Alpine system set up for a simple Go web application - it could extend to any other language but is not tested.

Pre-amble

This project is laid out with the static files and html inside a directory named ui within their own directories.

Before the setup and initialisation of Alpine and Tailwind, the directory looks something like this.

ui
├── html
│   ├── base.layout.tmpl
│   ├── footer.partial.tmpl
│   ├── home.page.tmpl
│   └── navbar.partial.tmpl
├── static
│   ├── css
│   │   └── main.css
│   └── js
└── ui.go

Installing and compiling Tailwind

First thing to do is install Tailwind. I use yarn but I’ve used npm here because its more widely used.

npm install -D tailwindcss 
# I also installed these tailwind plugins which I needed for the layout
#npm install -D @tailwindcss/forms @tailwind/aspect-ration
npx tailwindcss init

This creates a tailwind.config.js file which needs to be updated to reflect our directory structure and template file types.

module.exports = {
  content: ["./ui/**/*tmpl"], <-- THIS IS IMPORTANT
  theme: {
    extend: {},
  },
  plugins: [
      require('@tailwindcss/aspect-ratio'),
      require('@tailwindcss/forms'),
  ],
}

What tripped me up was the content: [] block. My Go templates use the following naming convention; <name>.<type>.tmpl. The types are layout,page and partial but it always ends with tmpl. Initially, I was using .html because I wasn’t paying attention and copying other guides. This time I am catching all tmpl files inside the ui folder.

I then created a npm script block entry into the package.json file.

{
  "scripts": {
    "build-css": "tailwindcss -i ui/static/css/main.css -o ui/static/css/theme.css -m",
  },
  "devDependencies": {
    "@tailwindcss/aspect-ratio": "^0.4.0",
    "@tailwindcss/forms": "^0.5.0",
  }
}

build-css will create a file called theme.css in the ui/static/css directory which is then referenced inside my base.layout.tmpl template. The -m minifies it, which I do for prod and dev because this file is never touched.

The above step is predicated on the existence of a file called main.css inside the ui/static/css directory. For clarity my main.css looks like this:

" https://tailwindcss.com/docs/installation - Step #3
@tailwind base;
@tailwind components;
@tailwind utilities;

Alternatively, you could run just run the compiler (build-css) in a bash script, CI pipeline or Dockerfile.

Alpine.js Set up

Next, bundle up Alpine inside the application without needed to use the CDN.

npm install alpinejs esbuild

Alpine needs to be instantiated, so I created an index.js at the root of the directory and called Alpine.

import Alpine from 'alpinejs';

window.Alpine = Alpine;

Alpine.start();

Then I add an extra script to the package.json which will compile Alpine using esbuild and drop it into the ui/static/js directory. Alpine, like tailwind is minified because I am not altering it in any way.

{
  "scripts": {
    "build-css": "tailwindcss -i ui/static/css/main.css -o ui/static/css/theme.css -m",
    "alpine": "npx esbuild index.js --outfile=ui/static/js/bundle.js --bundle --minify" # New
  },
  "devDependencies": {
    "@tailwindcss/aspect-ratio": "^0.4.0",
    "@tailwindcss/forms": "^0.5.0",
    "tailwindcss": "^3.0.23"
  },
  "dependencies": {
    "alpinejs": "^3.9.1", # New
    "esbuild": "^0.14.25" # New
  }
}

The final directory structure should look something like this

ui
├── html
│   ├── base.layout.tmpl
│   ├── footer.partial.tmpl
│   ├── home.page.tmpl
│   └── navbar.partial.tmpl
├── static
│   ├── css
│   │   ├── main.css
│   │   └── theme.css # New
│   └── js
│       └── bundle.js # New
└── ui.go

Testing it out

Remove any references to Tailwind or Alpine’s CDN and replace them with the location of theme. css and bundle.js respectively. Refreshing the application page should render everything correctly and any Alpine widgets should work.

The best part is the application is completely portable. For clarity, this application is using Go embedded files so that it is self-contained. After running go build you will be able to run it completely isolated and have the CSS and JS work as expected.

Keep up to date with my stuff

Subscribe to get new posts and retrospectives

Powered by Buttondown.