Migrating Sass Stylesheets from @import to the Module System
Learn how to replace Sass @import with the module system (@use/@forward) to avoid duplicate CSS, global namespace pollution, and name collisions, with a concrete migration example and verification steps.
13 Feb 2026, 11:37 UTC

Why the switch matters
If you still rely on @import to pull in Sass partials, every inclusion re‑evaluates the imported file. That can duplicate CSS output, pollute the global namespace, and make name collisions hard to trace. The Sass module system (@use and @forward) solves these problems by loading each stylesheet once, giving it an explicit namespace, and letting you control what gets exposed.
Core concepts
@use creates a namespaced module
When you write @use 'tokens', Sass treats tokens.scss as a module. Its members (variables, mixins, functions) are accessed via the module name or an alias you choose, e.g., tokens.$primary. By default the namespace is the filename’s last segment, but you can rename it: @use 'tokens' as t.
@forward re‑exports a curated API
A module that exists only to gather and expose other modules can use @forward. This lets you build a façade: @forward 'tokens', 'buttons' makes the public members of those modules available without exposing internal details.
Renaming, showing, and hiding
During @use you can rename members, show only a subset, or hide others: @use 'tokens' as t show $primary, $spacing hide $deprecated. This gives fine‑grained dependency control and prevents accidental use of private values.
Worked example: migrating a token file and a button component
Assume two existing partials:
/* _tokens.scss */
$primary: #0066ff;
$spacing: 1rem;
$deprecated: #999;
/* _buttons.scss */
@import 'tokens'; /* old style */
.btn {
color: $primary;
padding: $spacing;
}
Step 1 – rename the import to a module:
/* _buttons.scss */
@use 'tokens' as t;
.btn {
color: t.$primary;
padding: t.$spacing;
}
Step 2 – optionally hide the deprecated token:
@use 'tokens' as t show $primary, $spacing hide $deprecated;
Step 3 – compile with Dart Sass (≥ 1.23.0):
sass src/styles.scss dist/styles.css
The resulting CSS contains the variables’ values only once, and there is no global $primary or $spacing leaking into other stylesheets.
Trade‑offs and limitations
- Namespace prefix required: Every non‑private member must be referenced with its module name (or alias). Forgetting the prefix yields an “Undefined variable” error, which can feel verbose at first.
- No circular dependencies: Sass forbids a module from using another that, directly or indirectly, uses it back. Shared low‑level values (like tokens) must reside in a leaf module that has no upward
@use. - LibSass incompatibility: The original LibSass (Node Sass) never implemented modules. If your build still depends on
node-sass, you must switch to the Dart Sass implementation (sassnpm package).
You can verify a successful migration by checking that no @import statements remain for Sass files:
grep -r '@import.*\.scss' src/ # should return no matches
And confirm the compiler version:
sass --version # expect output like "1.78.0 dart-sass"
Actionable closing
Start small: pick a single leaf partial (e.g., your token file), convert its imports to @use with an alias, and update the files that consume it. Run the build, inspect the CSS for duplicate rules, and fix any namespace errors. Once that works, repeat the process upward, using @forward to create a clean public API for each layer. The module system gives you predictable scoping, eliminates duplicate output, and sets a clear path for future style‑scale growth.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.