ASP.NET Core Blazor + Tailwind CSS

This tutorial walks you through the basic steps of setting up ASP.NET Core Blazor and Tailwind CSS, providing you with the tools and knowledge necessary to start building beautiful, functional web applications. We will cover everything from setting up the development environment to implementing a basic user interface.

Tailwind CSS is a utility CSS framework that allows developers to quickly and efficiently design user interfaces with utility classes. Unlike other CSS frameworks, Tailwind focuses on offering low-level building blocks for custom layout, without imposing a default visual layout.

Getting Started

1. Create the new Blazor project

Let’s get started by creating a new Blazor WebAssembly Project

* It is necessary to have Node.js and npm CLI installed

2. Initialize new Node.js project

We execute the command npm init from the command prompt in the root of the project. This will initialize a new Node.js project, creating a file package.json that manages the project’s dependencies, scripts, and other metadata.

npm init
3. Add Tailwind CSS and other packages

In the terminal, run the following command:

npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

This command installs three Node.js packages in your project:

  • tailwindcss@latest: Install the latest version of Tailwind CSS.
  • postcss@latest: Installs the latest version of PostCSS, a tool used to transform CSS styles with JavaScript plugins. PostCSS is required to process Tailwind CSS files.
  • autoprefixer@latest: Install the latest version of Autoprefixer, a PostCSS plugin that adds automatic prefixes to CSS rules to ensure cross-browser compatibility.

By specifying @latest, you ensure that you get the most recent version of each package.

4. Configure PostCSS

We create postcss.config.js at the root of your project. This file defines how PostCSS should process your CSS, including the use of various plugins such as Tailwind CSS and Autoprefixer.

Open this file and configure it to use Tailwind CSS and Autoprefixer. Here’s a basic example of what it might look like:

module.exports = {
    plugins: {
        tailwindcss: {},
        autoprefixer: {},
    }
}
5. Configure Tailwind CSS

Run the command npx tailwindcss init that is used to initialize Tailwind CSS in your project.

npx tailwindcss init

This command creates a Tailwind configuration file, usually called tailwind.config.js, in the root of your project. This file is where you can customize your Tailwind CSS settings to the needs of your project.

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./**/*.razor', './**/*.cshtml'],
  theme: {
    extend: {},
  },
  plugins: [],
}

In the wwwroot folder, add a new CSS file and name it app.css. This is what PostCSS will use to generate your site’s CSS resource. Here we will add imports from the tailwind CSS library:

@tailwind base;
@tailwind components;
@tailwind utilities;
6. Create CSS with PostCSS CLI

Now, we need to add a script called buildcss that can automate the processing of PostCSS.

{
  "scripts": {
    "buildcss": "postcss wwwroot/css/app.css -o wwwroot/css/app.min.css"
  },
  "dependencies": {
    "postcss-cli": "^11.0.0"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.17",
    "postcss": "^8.4.33",
    "tailwindcss": "^3.4.1"
  }
}

The main purpose of this script is to take your main CSS stylesheet ( app.css), process it with PostCSS (which could include applying transformations like autoprefixing, minification, or applying Tailwind CSS utilities), and then save the result to a new file ( app.min.css), which is a version optimized for production use.

Now, you can run this script using the command npm run buildcss in your terminal.

npm run buildcss

Here the new file app.min.css:

7. Post-build event configuration

Configure your .NET project so that, after the project build completes, it automatically runs the command npm run buildcss. This is useful to ensure that your CSS files are processed and optimized every time you build your project, keeping your styles up to date and optimized for production without needing to manually execute this step.

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="npm run buildcss" />
  </Target>
8. Add the reference of the generated CSS file to the Blazor application
<link rel="stylesheet" href="css/app.min.css" />
9. Finally, you can add components

Let’s try adding an example Tailwind component to MainLayout.razor

@inherits LayoutComponentBase

<div class="bg-white">
    <header class="absolute inset-x-0 top-0 z-50">
        <nav class="flex items-center justify-between p-6 lg:px-8" aria-label="Global">
            <div class="flex lg:flex-1">
                <a href="#" class="-m-1.5 p-1.5">
                    <span class="sr-only">Your Company</span>
                    <img class="h-8 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="">
                </a>
            </div>
            <div class="flex lg:hidden">
                <button type="button" class="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700">
                    <span class="sr-only">Open main menu</span>
                    <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
                    </svg>
                </button>
            </div>
            <div class="hidden lg:flex lg:gap-x-12">
                <a href="#" class="text-sm font-semibold leading-6 text-gray-900">Product</a>
                <a href="#" class="text-sm font-semibold leading-6 text-gray-900">Features</a>
                <a href="#" class="text-sm font-semibold leading-6 text-gray-900">Marketplace</a>
                <a href="#" class="text-sm font-semibold leading-6 text-gray-900">Company</a>
            </div>
            <div class="hidden lg:flex lg:flex-1 lg:justify-end">
                <a href="#" class="text-sm font-semibold leading-6 text-gray-900">Log in <span aria-hidden="true">→</span></a>
            </div>
        </nav>
        <!-- Mobile menu, show/hide based on menu open state. -->
        <div class="lg:hidden" role="dialog" aria-modal="true">
            <!-- Background backdrop, show/hide based on slide-over state. -->
            <div class="fixed inset-0 z-50"></div>
            <div class="fixed inset-y-0 right-0 z-50 w-full overflow-y-auto bg-white px-6 py-6 sm:max-w-sm sm:ring-1 sm:ring-gray-900/10">
                <div class="flex items-center justify-between">
                    <a href="#" class="-m-1.5 p-1.5">
                        <span class="sr-only">Your Company</span>
                        <img class="h-8 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="">
                    </a>
                    <button type="button" class="-m-2.5 rounded-md p-2.5 text-gray-700">
                        <span class="sr-only">Close menu</span>
                        <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
                        </svg>
                    </button>
                </div>
                <div class="mt-6 flow-root">
                    <div class="-my-6 divide-y divide-gray-500/10">
                        <div class="space-y-2 py-6">
                            <a href="#" class="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50">Product</a>
                            <a href="#" class="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50">Features</a>
                            <a href="#" class="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50">Marketplace</a>
                            <a href="#" class="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50">Company</a>
                        </div>
                        <div class="py-6">
                            <a href="#" class="-mx-3 block rounded-lg px-3 py-2.5 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50">Log in</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </header>

    <div class="relative isolate px-6 pt-14 lg:px-8">
        <div class="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80" aria-hidden="true">
            <div class="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
        </div>
        <div class="mx-auto max-w-2xl py-32 sm:py-48 lg:py-56">
            <div class="hidden sm:mb-8 sm:flex sm:justify-center">
                <div class="relative rounded-full px-3 py-1 text-sm leading-6 text-gray-600 ring-1 ring-gray-900/10 hover:ring-gray-900/20">
                    Announcing our next round of funding. <a href="#" class="font-semibold text-indigo-600"><span class="absolute inset-0" aria-hidden="true"></span>Read more <span aria-hidden="true">→</span></a>
                </div>
            </div>
            <div class="text-center">
                <h1 class="text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">Data to enrich your online business</h1>
                <p class="mt-6 text-lg leading-8 text-gray-600">Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat fugiat aliqua.</p>
                <div class="mt-10 flex items-center justify-center gap-x-6">
                    <a href="#" class="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Get started</a>
                    <a href="#" class="text-sm font-semibold leading-6 text-gray-900">Learn more <span aria-hidden="true">→</span></a>
                </div>
            </div>
        </div>
        <div class="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]" aria-hidden="true">
            <div class="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
        </div>
    </div>
</div>

We compile the project and then execute the command dotnet watch run that starts the application and watches it to automatically restart it every time source code files are modified. If the project is not in the same folder as the solution, you can indicate it with --project

dotnet watch run --project src/8.x

And the result:

We are now ready to continue adding and testing tailwind components with Blazor.

Sample code

Blazor + Tailwind CSS Template