Understanding @use and @forward in Dart Sass
Table of contents

In recent years, CSS preprocessors have become very popular because they allow developers to write CSS programmatically. This has been a significant leap forward in flexibility and power when declaring styles. Sass is the most well-known and widely used among them. Although there are many other ways to style our web applications today—like CSS-in-JS in its many flavors, PostCSS, or CSS Modules—SASS is still widely used, as revealed by the latest The State of CSS 2021, and thus remains a relevant and powerful tool.
Module System
In 2019, Sass announced the Module System, a major core change that would phase out @import
and some global functions. The Dart Sass implementation (JavaScript-based and in development since 2016) already supported this new module system, so the Sass organization started promoting its use over LibSass and RubySass.
Despite being backward compatible and allowing both approaches to coexist for a while, Sass planned to permanently drop support for @import
and the old internal module system by October 1, 2022.
At that time, LibSass was still widely used (as a dependency of node-sass) but lacked full support for the new module system and was eventually deprecated, making the switch inevitable.
How does this affect me?
It depends. This means we’ll have a lot of legacy code that we’ll need to migrate, which you can do following this migration guide. But if something is working, why change it? Still, when using SASS in your new projects, you will need to use the new module system and thus understand the new rules.
@use and @forward
The most notable change in this version is the introduction of these two new directives, which replace @import
and eliminate the global scope where utilities used to live.
Previously, we included all partial files in a single stylesheet (like an index) that styled other components or parts of the application, along with utilities like variables, mixins, placeholders, and functions. All partials had access to and could use those utilities. But with the new module system, when a partial wants to access a utility, it must import it explicitly, giving us more control and making it immediately clear what external code is being used and where it comes from—very similar to JavaScript import
s.
And that’s not all. There are many other benefits.
Benefits
- Clarity. It’s much easier to know what is being used in each stylesheet and where it comes from, since everything must be explicitly declared at the top of the file.
- No global scope. Since there’s no longer a shared virtual space for variables, mixins, and functions, name collisions between your code and third-party code (like having a
row
mixin in both) are avoided. - Encapsulation. Imported files are encapsulated using a namespace, which helps avoid name clashes within the same file.
- Avoid duplication. With
@import
, you could import the same file multiple times, sometimes unintentionally, affecting the final CSS.@use
and@forward
protect against this. - Disambiguation. Since
@import
is also a native CSS rule, removing it avoids potential confusion. - Privacy. You can declare private members that can’t be used from outside the file, or selectively choose which ones to expose or hide during import.
- Third-party library flexibility. You can configure third-party libraries without overwriting them—especially useful if you’re the library author.
@use
@use 'theme';
.text {
color: theme.$primary-color;
}
Declare it at the top of the file where you want access to members (variables, mixins, functions).
By default, these members are namespaced by the file name. So to access the $primary-color variable from theme.scss, you must use theme.$primary-color. This allows you to use multiple $primary-colors from different files in the same stylesheet.
You can also rename the namespace using as:
@use 'theme' as defaultTheme;
@use '../3rd-party-library/theme' as externalTheme;
.box {
color: defaultTheme.$primary-color;
background-color: externalTheme.$primary-color;
}
Or remove the namespace using *, although Sass will throw an error if any conflicts occur between variables from different imports:
@use 'theme' as *;
@use '../3rd-party-library/theme' as *;
.box {
color: $primary-color; // from theme
background-color: $primary-color; // from 3rd-party-library/theme
}
You can also make members private (i.e., not accessible from outside) by prefixing them with - or _:
// _palette.scss
$primary-color: '#282828'; // accessible variable
@mixin _reset-list {
// private mixin, not accessible externally
margin: 0;
padding: 0;
list-style: none;
}
@forward
Used to bring the contents of another file into a file—like an index importing partials via @import.
Like @use, it protects against code duplication if called multiple times. But unlike @use, @forward does not add a namespace.
// General settings
@forward 'base/reset';
@forward 'base/reset'; // no duplicate code
@forward 'base/common';
// Common components
@forward '../components/';
Like @use, you can manage member privacy with @forward, but in this case by using the show or hide clauses to control what gets exposed:
@forward 'hero' show $hero-color; // import ONLY $hero-color
@forward 'utils' hide triangle; // import EVERYTHING except triangle() mixin
Built-in modules
Some of Sass’s built-in modules and utilities have changed—they may be new, renamed, or removed. Be sure to check the official documentation. The key takeaway is that now you must explicitly access modules using @use, and you can rename them:
@use 'sass:math' as builtInSassMathModule;
@use 'sass:color';
@use 'theme';
body {
&::before {
content: builtInSassMathModule.random();
color: color.alpha(theme.$color);
}
}
Configuring third-party libraries
With @use, you can configure third-party libraries without overwriting their values by using with at the time of import:
$custom-breakpoints: (
mobile: 375px,
tablet-small: 640px,
tablet: 780px,
tablet-large: 920px,
desktop: 1180px,
wide: 1400px,
);
$custom-show-breakpoints: (mobile, tablet, desktop, wide);
@forward '../node_modules/sass-mq/mq' with (
$breakpoints: $custom-breakpoints,
$show-breakpoints: $custom-show-breakpoints
);
Conclusions
Even though the module system brought many benefits, there wasn’t a mass migration, and a lot of code still uses @import. This is likely due to several overlapping trends, such as:
- The rise of shadow DOM and reusable component styling
- CSS-in-JS:
- Styled-components
- Emotion
- JSS
- PostCSS
- CSS Modules
- Tailwind (and other utility-first frameworks)
Because of these growing options and the fact that LibSass was still available and didn’t force a change, it remained popular (as shown by npm trends). But with the announcement that LibSass is going away and the change will be mandatory for new development, if you want to keep using SASS, it’s time to embrace the change and stop delaying the inevitable.
That’s why we’ve shared a link to a basic scratch repo with some comments to help you get started. :)
This is the link to the repository for trying it out and play a little around.
Footnotes
In a previous article about the CSS functions min
, max
and clamp
, we explained that to be able to use these functions in Sass we need to use them with quotation marks alongside the function unquote()
because Sass understands that these names are referred to its own functions since they shared the same name.
.element-c {
width: unquote("min(500px, 98%)");
}
.element-d {
font-size: unquote("max(min(10%, ((1.25vw + 10px) * 0.5), 5vw), 1rem)");
}
Anyway, I you have already read the article pointed out at the beginning you could guess why now it is not a problem and why you can write as normal with dart-sass.
.element-c {
width: min(500px, 98%);
}
.element-d {
font-size: max(min(10%, ((1.25vw + 10px) * 0.5), 5vw), 1rem);
}
These functions belong to the ‘math’ built-in module, and since by default when importing a module with @use
the members are encapsulated, there is no more collision between the CSS native function and the Sass one.
@use 'sass:math';
.element-c {
width: min(500px, 98%); /* CSS native */
}
.element-e {
width: math.min(500px, 300px); /* Sass function */
}
If you find it interesting
If you have any doubt or you want to chat about this topic, as if you find interesting the content or our profiles and you think we could build something together, do not hesitate to contact us trough the email address hola@mamutlove.com