Stop duplicating CSS with Sass @use and @forward
Replace @import with Sass modules to scope variables, deduplicate compiled CSS, and expose a single library entry point.
02 Oct 2026, 00:53 UTC

The problem: global variables and duplicate CSS
In a legacy Sass project, every partial is loaded with @import. That rule drops the file’s variables, mixins, and functions into a shared global namespace. If _buttons.scss and _forms.scss both define $primary, the last file loaded wins. The same partial imported by three different entry points also emits its CSS three times. You end up with bloated output, hidden coupling, and refactors that break unrelated components.
What @use changes
@use loads a Sass module once. Its members are available under a namespace unless you alias them, and the module’s CSS is included in the output only one time, no matter how many stylesheets load it. Members beginning with - or _ are private: the module can use them internally, but other stylesheets cannot see or reference them.
Terms: a module is any Sass file loaded with @use. A namespace is the prefix—usually the file name—that keeps its variables and mixins separate from your global scope.
Building a public entry point with @forward
@forward re-exports members from another module. This lets you keep internal files private while exposing a single public API. A common pattern is an _index.scss file that forwards component modules, so consumers write @use 'index' instead of importing a dozen partials individually.
Worked example: a button module
Create _button.scss:
$button-color: #0066cc !default;
$button-padding: 0.5rem 1rem !default;
@mixin button-styles($color: $button-color, $padding: $button-padding) {
.button {
background-color: $color;
border: none;
padding: $padding;
border-radius: 4px;
cursor: pointer;
}
.button:hover {
filter: brightness(90%);
}
}
Now load it from styles.scss:
@use 'button' as *;
@include button-styles(#cc0000);
Compile from the project root:
# Terminal, project root
# Requires: Dart Sass installed and on PATH
# Permissions: read src/, write dist/
dart-sass src/styles.scss dist/output.css
Expected checks:
dist/output.csscontains the.buttonrules exactly once.- Trying to reference
$button-colorin another Sass file produces a Sass “Undefined variable” error, because the variable is scoped to thebuttonmodule.
If you prefer an explicit namespace instead of as *, write @use 'button' and call @include button.button-styles. The explicit form is more verbose but makes the origin of each mixin obvious.
Migrating from @import
Sass ships a migration tool that rewrites @import statements to @use or @forward. Because it rewrites files in place, commit your work or back up the folder first.
# Terminal, project root
# Permissions: write access to src/
npx sass-migrator migrate --import 'src/**/*.scss'
After migration, recompile and compare the generated CSS against the previous build. If the output differs, inspect the transformed files for missing namespaces or aliases.
Limitations
The module system requires Dart Sass. Older implementations such as LibSass or Ruby Sass do not fully support @use and @forward. Also, because globals are no longer implicit, converting a large codebase means adding explicit @use statements to every file that relied on imported variables. That verbosity is the trade-off for predictable scoping.
Verify the result
- Compile a file that loads the same module in two places and confirm the CSS contains only one copy of its rules.
- Attempt to use a private member (prefixed with
-or_) from another file; compilation should fail. - Run
dart-sass --versionto confirm you are using the canonical implementation, not a legacy port.
Switching to Sass modules does not change how browsers read your CSS. It changes how you organize source files: styles are scoped, duplicates are removed, and component libraries get a single, intentional entry point.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.