Tailwind v4 with Blazor - It just got easier

3/3/2025
4 minute read

Tailwind v4 with Blazor - It just got easier

Tailwind version 4 was just released. With the new CLI, it is just so much easier to use tailwindcss with Blazor. So in this post, I will show you how to use the new CLI to create a new Blazor project with Tailwind v4.

Short desclaimer: If you still want to use version 3, I already wrote an article about this some time ago: "Blazor with TailwindCSS".

tailwind/cli

The basic installation setup is taken from the official documentation: https://tailwindcss.com/docs/installation/tailwind-cli I will link the sample repository at the end of this post so you can see the full setup.

But here the short gist:

  1. Create a new Blazor project You can either use your favorite IDE or the dotnet CLI to create a new Blazor project. I will use the dotnet CLI for this example:
dotnet new blazor -o BlazorTailwind
  1. Install the cli with tailwind:
npm install tailwindcss @tailwindcss/cli

This will create a package.json (and package-lock.json) file and a node_modules folder. Therefore I would recommend adding the node_modules folder to your .gitignore file. The package.json looks like this:

{
  "dependencies": {
    "@tailwindcss/cli": "^4.0.8",
    "tailwindcss": "^4.0.8"
  }
}
  1. Use tailwind

Inside your wwwroot/app.css add tailwind like this on the first line:

@import "tailwindcss";
  1. Generate the merged css file

We are almost done. The last step is to generate the generated CSS file which consists of all the tailwind classes and your custom definitions in your wwwroot/app.css file. You can do this by running the following command:

npx @tailwindcss/cli -i wwwroot/app.css -o wwwroot/dist.css

This will generate a dist.css file in your wwwroot folder. You can now include this file in your App.razor instead of the app.css file like this:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <base href="/"/>
    <link rel="stylesheet" href="dist.css"/>
    <link rel="stylesheet" href="@Assets["BlazorTailwind4.styles.css"]"/>

Now let's go to our Home.razor, use some tailwind classes and run the npx command again:

<h1 class="p-4 font-bold text-blue-600">Hello, world!</h1>

Will result in:

Hello World

Now you might have seen, that I did not use the @Assets directive. And there is a reason. The process I showed is very manual and you have to run the command every time you change something in your app.css file. But there is a better way to do this.

Automate the process

We can use the npx command to get executed everytime we build the project. For that let's go to our package.json:

{
    "dependencies": {
        "@tailwindcss/cli": "^4.0.8",
        "tailwindcss": "^4.0.8"
    },
    "scripts": {
        "build:css": "npx @tailwindcss/cli -i wwwroot/app.css -o wwwroot/base.css"
    }
}

With scripts we can execute commands. So we can now run build:css everytime we build. To do that, just add the following line to your .csproj file:

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="npm run build:css" />
</Target>

Now everytime you build your project, the dist.css file will be generated. That said, you might want to gitignore the dist.css file, because it will be generated everytime you build the project anyway. No need to keep the autogenerated file in your repository.

GitHub Actions

The unfortunate thing is, that you have to have node installed on your machine or your GitHub action. So to make a minimal CI pipeline work, you wanto have something like this:

name: Build

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: windows-latest
    permissions:
      contents: read

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
      - run: npm install

      - name: Set up .NET Core
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.x'

      - name: Build with dotnet
        run: dotnet build --configuration Release

dotnet watch

If you are like me, you are using dotnet watch aka hot-reload all the time. But there is an inherit problem with tailwindcss and the stuff we built up earlier. dotnet watch does not invoke the pre-build event. So to come around that fact you basically need two shells open. The first one for your dotnet watch and the second to let tailwind watch your code:

npx @tailwindcss/cli -i wwwroot/app.css -o wwwroot/dist.css --watch

Now tailwind automatically recognizes changes in your app.css file and updates the dist.css file. Of course you can also park that in your package.json:

{
  "dependencies": {
    "@tailwindcss/cli": "^4.0.8",
    "tailwindcss": "^4.0.8"
  },
  "scripts": {
    "build:css": "npx @tailwindcss/cli -i wwwroot/app.css -o wwwroot/base.css",
    "watch:css": "npx @tailwindcss/cli -i wwwroot/app.css -o wwwroot/base.css --watch"
  }
}

And just call npm run watch:css

Resources

  • Source code to this blog post: here
  • All my sample code is hosted in this repository: here

Blazor .NET 8 - Enhanced Form Navigation

There are many new cool features with .NET 8 and Blazor in particular. In this blog post, I want to highlight a feature that I believe is very useful in the new context Blazor is living.

Blazor with TailwindCSS

Ever wondered how to use TailwindCSS with Blazor? Let's make it work!

.NET 8 and Blazor United / Server-side rendering

New .NET and new Blazor features. In this blog post, I want to highlight the new features that are hitting us with .NET 8 in the Blazor world. So let's see what's new.

An error has occurred. This application may no longer respond until reloaded. Reload x