Build Tailwind v4 + Daisy UI with Dotnet + Docker

Date: 2025-03-31 | containers | craft | create | docker | dotnet | falco | tailwind |

DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

I use Tailwind to style most of my apps as I find it fast to work with, flexible, and it has a ton of UI examples on the web. Tailwind v4 changed how the build and configuration system works so I needed to upgrade from my previous Tailwind + Dotnet + Docker guide.

In this post we'll walk through using Tailwind v4 to style ASP.NET web apps using Docker containers for orchestrating the build.

Project Overview

In previous posts we built an example fullstack app using F# + Falco. We'll be using this app as a foundation to showcase adding Tailwind styles to it.

Note: F# builds / runs on dotnet and Falco is a lean wrapper around ASP.NET so the same steps will apply to a C# app or app running a different flavor of ASP.NET.

Previous posts in the series:

HAMINIONs Members get full access to the project files (GitHub) as well as dozens of other example projects we walk through here on the blog. You also support me making more of these posts - thank you.

Tailwind v4 with Dotnet and Docker

Tailwind + Docker Build Pipeline

Tailwind is a pretty big style library so for production usecases it's highly recommended to use a build tool to help prune the styles down to just the styles your app needs so it's a more reasonable size to send to the client. This pruning is usually done at build time which implies a build step to kick it off and bundle the output styles with your app.

I have a Tailwind folder in my project that holds the Node project for Tailwind. Tailwind v4 specifies the source files and configuration (including plugins like typography and daisyui) in the css.

tailwind.css

/* hamy - Yes this is invalid "css" but this works with the CLI */
@import "tailwindcss" source("../../Source");
@plugin "@tailwindcss/typography";
@plugin "daisyui";

package.json

{
  "name": "tailwind",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "NODE_ENV=development npx @tailwindcss/cli -i ./Styles/tailwind.css -o ./out/tailwind.css"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@tailwindcss/cli": "^4.0.12",
    "@tailwindcss/typography": "^0.5.16",
    "daisyui": "^5.0.0",
    "tailwindcss": "^4.0.12"
  }
}

I already use Docker to containerize my apps so it makes sense to leverage it to handle my Tailwind build step as well.

  • Build Tailwind in a Node layer
  • Build dotnet app in a dotnet layer
  • Create a new layer for running the aspnet app - Copy the build artifacts, place tailwind css in wwwroot (static files folder), then run the app

Dockerfile:

# Set up Tailwind

FROM node:23-slim as styles

COPY ./Tailwind ./
RUN npm install

COPY ./ ./Source
RUN npx @tailwindcss/cli -i ./Styles/tailwind.css -o ./out/tailwind.css

# Build Dotnet App

FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim AS build
EXPOSE 80

WORKDIR /source

## copy csproj and restore as distinct layers
COPY ./*.fsproj ./
RUN dotnet restore

## copy and publish app and libraries
COPY . .
RUN dotnet publish -c release -o /app

# Copy build / styles into final location

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app .
COPY --from=styles ./out/tailwind.css ./wwwroot/css/tailwind.css
ENTRYPOINT ["dotnet", "web.dll"]

Styling Dotnet Frontends with Tailwind v4

How you style your frontends with Tailwind will depend on how you've built your frontends. Personally I'm a fan of Multi-Page Applications so server-side render most of my UIs / HTML. For those cases I can simply serve my Tailwind css static file from my backend, reference it in my HTML, and then use styles.

Here's how this looks using F# + Falco with Falco.Markup as an HTML DSL (which is how I build most of my apps these days).

To serve my static files (in wwwroot) I call UseStaticFiles in my app builder. I'm using Falco here but this works on most aspnet-based apps.

let configureApp (app: IApplicationBuilder) =
    app
        .UseStaticFiles() // Static files in wwwroot
        .UseRouting()
        .UseFalco(webAppEndpoints)
    |> ignore

To reference the Tailwind css in my HTML, I use my base HTML template to pull in common dependencies like styles. Here's how this looks with Falco.Markup as an HTML DSL.

let renderWithBaseLayout
    (headSlot: XmlNode list)
    (bodySlot: XmlNode list)
    (footerSlot: XmlNode list)
    =
    Elem.html [] [
        Elem.head [] [
            Elem.meta [
                Attr.charset "UTF-8"
                Attr.name "viewport"
                Attr.content "width=device-width, initial-scale=1"
            ]
            Elem.link [
                Attr.rel "stylesheet";
                Attr.href "/css/tailwind.css";
            ]
            yield! headSlot
        ]
        Elem.body [] bodySlot
        Elem.footer [] footerSlot
    ]

Finally to use the Tailwind classes themselves, I just use the class on my HTML elements:

let getAllHttpHandler: HttpHandler =
    fun (ctx: HttpContext) -> 
        task {
            let count = itemRepo.GetAll().Length

            let itemListMarkup = 
                Elem.div [Attr.class' "max-w-prose mx-auto p-6"] [
                    Elem.h3 [
                        Attr.class' "text-lg"
                    ] [ Text.raw $"ItemCount: {count}" ]
                    Elem.table [ Attr.class' "table table-zebra" ] [
                        yield! 
                            itemRepo.GetAll()
                            |> List.map (fun item -> 
                                Elem.tr [] [
                                    Elem.td [] [Text.raw (item.Id.ToString())]  
                                ]
                            )
                    ]
                ]

            return 
                Response.ofHtml
                    (
                        renderWithBaseLayout
                            [] 
                            [itemListMarkup]
                            []
                    )
                    ctx
        }

Next

So that's how I use Tailwind v4 with dotnet and Docker.

As a reminder: HAMINIONs Members get full access to the project files (GitHub) as well as dozens of other example projects we walk through here on the blog.

Also CloudSeed (my F# project boilerplate) includes Tailwind v4 and DaisyUI so if you want to spin up a fullstack F# webapp in a few minutes check that out.

If you liked this post you might also like the previous posts in this series:

Want more like this?

The best way to support my work is to like / comment / share for the algorithm and subscribe for future updates.