Sass @use and @forward: Replacing @import for Namespaced Stylesheet Dependencies
Sass @import creates a global namespace where variables, mixins, and functions collide. Learn how @use and @forward enforce explicit dependencies, cache modules, and define a clear public API.
07 Jun 2026, 11:52 UTC

The global namespace problem
You add a new Sass partial, define $primary, and suddenly an unrelated card component changes color. Or you import the same utility file in three places and the compiled CSS contains three copies of the same rules. Both symptoms come from @import: it inlines a file and merges every variable, mixin, and function into one flat global scope. Nothing is namespaced, so collisions are silent and load order becomes load-bearing.
Sass modules replace that model with explicit dependencies. A module is loaded once, its members live under a namespace, and you choose what downstream files can see. The result is stylesheets that behave more like imported libraries in a programming language than like a pile of text includes.
@use loads a module once, under a name
@use pulls in a Sass file—called a module—and prefixes its members with the file's basename. If you write @use "tokens";, you access variables as tokens.$color-primary and call mixins as @include tokens.visually-hidden;. Functions work the same way: tokens.rem(16). The prefix is mandatory unless you alias it with as.
Two properties matter for engineering decisions:
- Single load. Modules are cached during a compilation. If five files
@usethe same module, it is evaluated once and its CSS is emitted once. - No global leakage. Members stay behind the namespace. You cannot accidentally overwrite
tokens.$color-primaryfrom another file.
Ordering is strict: @use rules must appear before any non-plain CSS rules in the file. Plain CSS @import (for web fonts, for example) is exempt, but Sass rules and style blocks must come after your @use declarations.
@forward defines a public API
If every consumer must @use five different partials, the dependency graph leaks into every file. @forward solves this by letting one module re-export selected members from others. Think of it as an index or barrel file.
A file named _api.scss might contain:
@forward "tokens";
@forward "buttons" show button-base;
Downstream, you write @use "api"; and access api.$color-primary or @include api.button-base;. The show keyword restricts the public surface; hide does the opposite. This lets you refactor internals without breaking consumers.
One limitation: @forward exposes API members only. It does not emit the side-effect CSS of the forwarded module into the importer. If a partial contains global style rules, those rules still belong to that module's own output.
Worked example: tokens, components, and an index
Suppose you have three files in scss/:
_tokens.scss
$color-primary: #005fcc;
$space-md: 1rem;
_buttons.scss
@use "tokens";
.button {
padding: tokens.$space-md;
background: tokens.$color-primary;
}
_index.scss
@forward "tokens";
@forward "buttons";
Your entry point, main.scss, consumes the index:
@use "index";
.hero {
color: index.$color-primary;
}
Compile from your project root with Dart Sass:
sass scss/main.scss dist/main.css
Expected check: open dist/main.css and confirm that #005fcc appears in both .button and .hero. If you add a second @use "index"; in another partial, the tokens and button rules should still appear only once in the final CSS.
Risk: This syntax requires Dart Sass or the modern Sass JS implementation. If your toolchain still uses node-sass or LibSass, the compiler will reject @use. Run sass --version before relying on modules.
Trade-offs and migration cost
The module system is not a drop-in replacement. Migrating from @import usually means restructuring partials and updating every reference across the project. A variable that was globally visible as $primary now requires a prefix like tokens.$primary. In a large codebase, that is a mechanical but wide-reaching change.
You also lose the ability to conditionally import a module inside a rule or mixin. @use is static and top-level. If your build relied on dynamic path construction with @import, that pattern needs rethinking.
Finally, the namespace prefix can feel verbose. Use as to shorten it when a name is unwieldy: @use "tokens" as t; lets you write t.$color-primary.
Where to start
Audit your partials for global variables and mixins that are shared across files. Move those into a leaf module—something with no internal Sass dependencies—and @use it from the files that need it. Once the leaf is stable, add a @forward index so consumers have a single entry point. Verify the result by inspecting the compiled CSS for duplicate rules and by confirming that sass --version reports Dart Sass.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.