Sass @use: Namespaces, Single-Load Semantics, and Theming a Component Library
Sass's @use module system replaces @import with namespaced, load-once modules and a with() configuration API. Here's how to structure a themeable library and what migration costs.
17 May 2026, 23:50 UTC

If your Sass codebase still runs on @import, you're accumulating two kinds of debt: deprecation warnings from Dart Sass (which has marked @import for removal in a future major release) and the subtler problem the module system was designed to fix: every partial you import lands in one global namespace, and every file that imports the same partial re-emits its CSS.
The takeaway: @use gives you explicit namespaces, load-once semantics, and a clean configuration API via with (...). Migrating is worth doing deliberately, not mechanically — this post shows the shape of a modern setup and the trade-offs you'll hit.
What @use actually changes
With @import, variables, mixins, and functions from a partial spill into the global scope of whatever file imports them. Two libraries defining $primary? Last one wins, silently. With @use, every module's members live behind a namespace derived from its filename:
// _colors.scss
$primary: rebeccapurple;
// button.scss
@use 'colors';
.button {
background: colors.$primary;
}Two more rules matter. First, each module is loaded once per compilation, no matter how many files @use it — so a partial containing actual CSS rules no longer gets duplicated into your output the way it did when ten entry points all @imported it. Second, @use rules must appear before any other statements in the file (aside from @charset or variable declarations used in configuration), which forces dependencies to be explicit and top-of-file.
One more modern touch: built-in modules like sass:math and sass:color are loaded the same way. Global functions like lighten() and the old / division operator are deprecated in favor of color.adjust() and math.div().
Worked example: a themeable component library
A common pattern is a design system split into small internal files but exposed through a single entry point. @forward makes that possible — it re-exposes another module's members so consumers see one clean API.
// theme/_tokens.scss
$color-brand: #5b4bc4 !default;
$spacing-md: 1rem !default;
// theme/_mixins.scss
@use 'tokens';
@mixin card {
padding: tokens.$spacing-md;
border: 1px solid tokens.$color-brand;
}
// theme/_index.scss
@forward 'tokens';
@forward 'mixins';The !default flags mark values as overridable. A consumer configures the whole library at load time:
// app.scss
@use 'theme' with (
$color-brand: #c44b5b
);
@use 'theme' as t;
.product-card {
@include t.card;
color: t.$color-brand;
}A few things to notice:
- The
with (...)block must appear on the first load of the module, before anything else uses it — configuration happens once, at load time. @use 'theme' as trenames the namespace, keeping member references short (t.$spacing-md).- Each component file states its dependencies explicitly. No more hunting for where a mystery variable was defined.
You can also control the forwarded API surface: @forward 'tokens' hide $internal; or @forward 'tokens' as token-*; to prefix members. This is how a library keeps internals private while presenting one entry point.
Trade-offs and limitations
The module system is a Dart Sass feature. LibSass and node-sass are deprecated and never implemented @use, so if your build chain still runs on those, step one is switching compilers — no amount of stylesheet refactoring helps until then.
Migration itself is not purely mechanical. Under @import, files often rely on variables being implicitly available because some earlier file happened to import the right partial. Under @use, every file must load what it references. On a large codebase that means real restructuring, not search-and-replace. The official sass-migrator tool automates much of the conversion and supports incremental migration, but expect to review its output — especially around configuration order and files that both define and emit CSS.
Also note that deprecation timelines and exact warning wording are version-sensitive; check the output of your installed Dart Sass version rather than assuming a specific schedule.
Verify it works
Three quick checks you can run locally (requires Dart Sass installed; run from your project root):
sass --version— confirm you're on Dart Sass, not the deprecated node-sass.- Create a partial with
$color: blue !default;, load it via@use 'x' with ($color: red);, and confirm the compiled CSS uses red. - Compile a file that
@uses the same CSS-emitting partial from two places and confirm the rules appear once in the output — then compare with the old@importbehavior to see the duplication you just eliminated.
Start with one self-contained partial, convert it to @use/@forward, and let the sass-migrator handle the mechanical bulk. Explicit dependencies feel verbose for a day; after that, they read like documentation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.