Migrating from Sass @import to @use/@forward for Encapsulated Stylesheets
Learn how to replace Sass @import with @use/@forward to get explicit namespaces, true encapsulation, and a cleaner stylesheet architecture.
21 Sept 2025, 20:05 UTC

The problem with @import
When a stylesheet grows, developers often split it into many partial files and pull them together with @import. Because @import inserts the contents of each file into the global scope, every variable, function, or mixin becomes visible everywhere. This leads to accidental name clashes, makes it hard to reason about where a value originates, and prevents true encapsulation of library code.
Thesis: @use and @forward give you explicit namespaces and controlled re‑exports
The @use rule loads a module once and makes its members available under a namespace you choose (by default the filename without the leading underscore). @forward lets a module re‑export selected members from another module, letting library authors hide internal details while exposing a clean public API. Together they eliminate global namespace pollution and provide compile‑time encapsulation.
How to migrate a simple project
Identify the entry stylesheet (e.g.,
styles.scss) and the partials it imports.Replace each
@import 'partial'with@use 'partial' as *;only if you need a quick, temporary fix; otherwise give it a meaningful namespace.Update all references to the imported members to use the namespace:
$colorbecomespartial.$color(or the custom namespace you set).If you are creating a library, move internal helpers into files prefixed with
-or_and forward only the public API via@forward.
Worked example
Suppose we have two files:
// _tokens.scss
$primary: #0066ff;
$spacing: 1rem;
// _mixins.scss
@mixin button($bg) {
background-color: $bg;
padding: $spacing;
border-radius: 4px;
}
The old entry point used @import:
// styles.scss (old)
@import 'tokens';
@import 'mixins';
.btn {
@include button($primary);
}
We migrate to @use with explicit namespaces:
// styles.scss (new)
@use 'tokens' as t;
@use 'mixins' as m;
.btn {
@include m.button(t.$primary);
}
Compile with Dart Sass (≥1.23):
sass styles.scss styles.css
The generated CSS contains no trace of the namespace; the namespaces exist only at compile time to prevent collisions.
Trade‑off and limitation
The main trade‑off is verbosity: every reference now requires a namespace prefix. This can feel cumbersome when migrating a large codebase with many shallow imports. However, the verbosity is a direct benefit—it makes dependencies explicit and searchable. A practical way to check that the migration succeeded is to search the compiled CSS for any unexpected class names or properties that could only arise from a name clash; if none appear, the encapsulation is working.
Actionable closing
- Run
sass --versionto confirm you are using Dart Sass 1.23 or newer. - Pick one stylesheet, replace its
@importstatements with@useusing meaningful namespaces, and update the references. - Re‑compile and verify the output matches the previous CSS (ignoring whitespace).
- Repeat file‑by‑file, then consider creating a
_public.scssthat forwards only the parts you want to expose to consumers.
By adopting @use and @forward, you gain predictable scoping, easier maintenance, and a foundation for future library distribution without the hidden pitfalls of the legacy @import system.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.