Configure TailwindCSS with Blazor
I had to make a project for school with Blazor WASM and I wanted to use TailwindCSS with it. A new Blazor project is quickly setup, but it uses Bootstrap out of the box, so how do we configure it to use TailwindCSS?
A little heads up. It’s not that easy, if you plan on using the basics, you could also use the CDN, but if you plan on using more advanced features in the configuration like using Sass and @apply
classes or purging, it’s worth it to find a solution.
Starting a new Blazor project
Starting a new blazor project is very easy to do. First, we have to install the dotnet SDK, which can be done easily here. It’s a very straight forward process.
After that, we can bootstrap the default Blazor WASM template by running
dotnet new blazorserver -o yourAmazingApp --no-https
that will create a new directory called yourAmazingApp
in the location you ran the command in.
Now we can start up the project in watchmode by running
dotnet watch run
Preparing a package.json
So now we have a basic project, but no Tailwind configuration or even a package.json
file. Since Blazor isn’t really meant to do this stuff, we have to make a workaround, but it works fine.
Theoretical solution
We can create a little local project with Yarn and use that to download our node_modules. Then we use Webpack to compile and export our Tailwind.scss
file to a main.css
file in the wwwroot/css
folder.
Adding the package.json file
We create a folder called assets
in our root directory of our .csproj
. in that folder, we can run npm init
or yarn init
. For the rest of this post I’ll use Yarn, but feel free to use NPM, it should also work.
so when we initialize a package.json
file we can add some dependencies. Below are some I used, but if you’re not using Sass, you can leave out some.
"devDependencies": {
"postcss-import": "^14.0.0",
"webpack-fix-style-only-entries": "^0.5.1",
"autoprefixer": "^10.2.4",
"css-loader": "^3.2.0",
"mini-css-extract-plugin": "^0.8.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss": "^8.2.6",
"postcss-easy-import": "^3.0.0",
"postcss-loader": "^4.2.0",
"sass": "^1.32.8",
"sass-loader": "^10.1.1",
"style-loader": "^1.0.0",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",
"webpack-watch-files-plugin": "^1.0.3"
},
"dependencies": {
"postcss-cli": "^8.3.1",
"postcss-nested": "^5.0.3",
"tailwindcss": "^2.0.3"
},
Now we have our dependencies installed, we can create some configuration files.
Configuration files
postcss.config.js
the postcss.config.js
file is used to process tailwind to our custom stylesheet. Here it also imports some other things, but feel free to only add require('tailwindcss')
.
module.exports = {
plugins: [
require("postcss-easy-import")({
prefix: "_",
extensions: [".css", ".scss"],
}),
require("tailwindcss"),
require("autoprefixer"),
require("postcss-nested"),
],
};
webpack.config.js
In the webpack.config.js
file we basically tell Webpack to take our raw /assets/scss/tailwind.scss
file and compile it to a main.css
file and add it to the wwwroot/css/
folder
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries");
module.exports = (env, args) => ({
devtool: args.mode === "development" ? "source-map" : "none",
entry: "./scss/tailwind.scss",
output: {
filename: "[name].js",
path: path.resolve(__dirname, "..", "wwwroot/css"),
},
module: {
rules: [
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{ loader: "css-loader", options: { url: false, sourceMap: true } },
{ loader: "postcss-loader" },
{ loader: "sass-loader" },
],
},
],
},
plugins: [
new FixStyleOnlyEntriesPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css",
}),
],
});
tailwind.config.js
below is the basic configuration of TailwindCSS without many to it. But from here we can customize it in any way we want like normal.
module.exports = {
purge: [],
theme: {
extend: {},
},
variants: {},
plugins: [],
};
Configure Blazor to use TailwindCSS
So now we have our compiled main.css
file, but it won’t trigger when we build or run our dotnet project. For that, we have to create a separate file called assets.targets
which will tell our dotnet project to trigger the package.json
build scripts.
<Project>
<ItemGroup>
<StaticAssets Include="$(MSBuildThisFileDirectory)**" Exclude="$(MSBuildThisFileDirectory)node_modules\**" />
<UpToDateCheckInput Include="@(StaticAssets)" />
</ItemGroup>
<PropertyGroup>
<StaticCSSPath>scss\tailwind.scss</StaticCSSPath>
</PropertyGroup>
<!-- If lockfile has changed, perform a new yarn install -->
<Target Name="yarnInstall"
Inputs="$(MSBuildThisFileDirectory)yarn.lock"
Outputs="$(BaseIntermediateOutputPath)yarn.lock">
<Message Importance="high" Text="Restoring dependencies using yarn. This may take several minutes..." />
<Exec Command="yarn" WorkingDirectory="$(MSBuildThisFileDirectory)" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)yarn.lock"
DestinationFolder="$(BaseIntermediateOutputPath)"
SkipUnchangedFiles="true"/>
</Target>
<!-- If any source file in this dir or below has changed, perform a Webpack build -->
<Target Name="BuildStaticAssets"
DependsOnTargets="yarnInstall"
BeforeTargets="CoreBuild"
Inputs="@(StaticAssets)"
Outputs="$(MSBuildThisFileDirectory)..\wwwroot\$(StaticCSSPath)">
<Exec Command="yarn build:$(Configuration)" WorkingDirectory="$(MSBuildThisFileDirectory)" />
</Target>
</Project>
now add the build scripts in package.json
and we’re pretty much done.
"scripts": {
"build:Debug": "webpack --mode development",
"build:Release": "webpack --mode production"
},
from this point on we can use Tailwind in any way we want. First, we have to add it to our project by modifying our .csproj
file.
yourAwesomeProject.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
// default configuration here ...
<Import Project="assets\assets.targets" />
</Project>
Run your project
voila! Now every time you run dotnet build
or debug your project. The scripts will run or at least checked. If everything goes correct, you should see the main.css
file in your /wwwroot/css
folder!
Now we can include and use it in our markup
Pages/_Host.cshtml
:
<head>
<meta charset="utf-8" />
<title>YourAwesomeProject</title>
<link href="~/css/main.css" rel="stylesheet" />
</head>
Also don’t forget to put that file in your .gitignore file
otherwise you’ll push and pull a file that gets generated, which is useless.