How to add SCSS support to Blazor

Joren Thijs
7 min readJun 18, 2021

As an Angular developer I am very excited about Blazor. Finally a way to run one of my favorite languages directly in the browser. And now with Microsoft releasing .NET 5 we can finally use component level CSS isolation. But what if you are like me and want to use Sass or SCSS instead of just plain old CSS?

Unfortunately if you want to use SCSS you are gonna have to add it yourself.
Luckily i can show you how ;)

You can find the demo project for this article on github.

1. First lets look at what we are trying to achieve:

In our Blazor app we have a global app.css file. This file contains all the global styles for our app.

Blazor 3.2 Project structure

We want to replace this with a global app.scss file that gets compiled into an app.css that our browser can use. This way we have to write less CSS.

With .NET 5 we can also use component CSS isolation. With this we can define styles that are unique to that component.

Blazor 5.0 Project structure

Once we build our app, the Blazor framework will take the styles definded in these component CSS files and add unique identifiers to them to make sure they only apply to that component. Those styles are then bundled together into a special project_name.styles.css file that we can include in our index.html file.

If we want to keep using this feature we need to define our own component SCSS files and compile them to a CSS file.

2. Existing solutions:

If you search for Blazor + SCSS you will find a few options floating around. These mostly range from Visual Studio extensions to Nuget packages but they have some limitations, let’s look at them first.

For visual studio extensions the main ones are Webcompiler and CompileSass.
WebCompiler compiles SCSS and Less files but is aimed at compiling all styles into one file. It’s very handy for our first use case and even comes with a nuget package to add a build task to MS Build. But for our second use case it falls short.
CompileSass on the other hand compiles all .scss into .css files and looks like it should work. Unfortunatly the project has not been updated since 2017 and the Sass language folks have not been sitting still.

There is also an interesting Nuget package called Delegate.SassBuilder wich adds a build task to MS Build and automatically compiles all .scss into .css files. Unfortunatly this project looks abandoned since it has not been updated since 2016.

At the time of writing (Oktober 2020) the three above were the only options but sinds then a promising new Nuget package has appeared. LibSassBuilder by Johan looks like it could get the job done although I have not tried it yet.

3. The correct approach

If we want to use the latest Sass features we must choose a Sass-compiler that is up to date. At the time of writing our best choice is using the Dart-Sass compiler.

The Dart-Sass compiler is fast, has no dependencies and can be installed as a CLI tool on any operating system.

For adding the Dart-Sass compiler to this project i will be using Node.js and NPM. This makes it easier for my colleagues who also work on this project to add and keeps my CI Pipeline simple. If you don’t want to use Node.js you can just install it as a global CLI Tool and still follow allong.

4. Adding SCSS support to Blazor

First we need to install the Dart-Sass compiler. For this we need to install Node.js and run npm init -y in our project folder to create a package.json file.

Next we run npm install sass --save-dev to install the sass compiler as a development dependency. Then we add sass to our npm scripts section so we can call it using the npm run command.

"scripts": {
"sass": "sass"
}

If you installed the Dart-Sass compiler as a CLI tool then you can of course skip these steps.

After that step our package.json file looks like this:

Next we need to add some tasks to MSBuild so it can compile our global SCSS files into CSS files when we build the project. To keep things organized i will be placing my global styles for app.scss into a Styles folder. I will also be using partial files to keep my actual app.scss organized.

To add a MSBuild task for our app.scss we need to add the following to our project file.

This will compile our app.scss into a app.css file and place it into wwwroot/css
Note the BeforeTargets="Compile" this will tell MSBuild to compile our SCSS before compiling our code. We also added a name to this action Name="CompileWwwrootSass" this will help us later when adding support for component .scss files.

Great! We now can compile our global scss files. But we see the CSS it produces is not minified. To achieve this we must add the--style=compressed flag.

npm run sass -- --style=compressed Styles:wwwroot/css

Before we dive into component level styles. I would like to be able to declare some Core SCSS files that contain SCSS variables off my app. Here I can put my colors and global mixins.

For this I will add a Core folder inside my styles folder.

Core styles folder

Then in my app.sccs I just import these like so:
(note my _app-variables.scss already imports my _app-colors.scss)

Import of scss variables in my app.scss

5. Adding component level SCSS files.

Great! The next thing to do is add support for component level CSS isolation.
In our project we have components with their own scoped SCSS file like so:

Example component file with scope scss file.

The idea is that only this specific file is compiled to it’s CSS equivalent and placed in the exact same location so the compiler can find it later. To do this we must first collect all these files in MSBuild by adding the following Item to our csproj file

<ItemGroup><ComponentScssFiles Include="**/*/*.scss" Exclude="node_modules/**;wwwroot/**;Styles/**" /></ItemGroup>

As you can see we are excluding precompiled SCSS files from libraries in our node_modules and wwwroot folder. We are also excluding the global stylesheets in our Styles folder.

Now that we have these files we want to tell the sass compiler to only compile these files and place them in their own directories ending with a .css extension. For this we need to add this command to our csproj.

Command="npm run sass -- --style=compressed --no-source-map 
--load-path=Styles/Core %(ComponentScssFiles.Identity)
%(relativedir)%(filename).css"

Note the --style=compressed flag to minify the output. the — -no-source-map flag will disbale sourcemaps since we don’t want these here. Last we added the --load-path=Styles/Core flag, this tells the compiler that to resolve imports it should look in the Styles/Core folder. This enables us to import our _app-variables.scss into our component level SCSS files.

Example of home.razor.scss

Great! except now the compiler is not ignoring partial sass files that start with an underscore anymore. To fix this we are going to add a condition that checks the file name against a regular expression.

Condition="!$([System.Text.RegularExpressions.Regex]::IsMatch
('%(ComponentScssFiles.Identity)', `.*[/\\]_.*`))"

So our full MSBUILD command looks like this:

<Exec Condition="!$([System.Text.RegularExpressions.Regex]::IsMatch
('%(ComponentScssFiles.Identity)', `.*[/\\]_.*`))"
Command="npm run sass -- %(ComponentScssFiles.Identity) %(relativedir)%(filename).css" />

Next we need to wrap this in build target property and tell the compiler to compile our scoped SCSS files as well. In my example I will tell the compiler to compile the scoped files before the global files. In the end my full project file looks like this.

<script src=”https://gist.github.com/Joren-Thijs/950855a3d1e9fff76f46ca09b9cd6f99.js"></script>

6. Dotnet Watch support

As a final small point I want dotnet watch to reload the page when I make SCSS edits. To do this we simply need to add SCSS files to dotnet watch in our csproj file. (Note we are excluding CSS files so compiling SCSS files does not trigger a new watch rebuild)

<script src=”https://gist.github.com/Joren-Thijs/d1e1431d30bc70fbd99f004ab6654f27.js"></script>

7. Cleanup VS Code

You may notice you suddenly have a bunch of ugly minified CSS files sitting in your file explorer in VS Code. Luckily we can hide these by adding a settings.json file to our .vscode folder and do some simple pattern matching.

<script src=”https://gist.github.com/Joren-Thijs/8b9415faf1e69c43b7514dce8a48a274.js"></script>

There! Now you are all set to use a powerful sass system in your Blazor app.
I Hope you learned something new. You can find the code for this example on github.

--

--