devcrea.com
Web & Frontend

Sass @import Deprecated: How to Migrate to @use and @forward

Sass @import is deprecated in Dart Sass and will eventually break your build. Learn why it happens, what breaks, and how to migrate to @use and @forward.

Sass @import Deprecated: How to Migrate to @use and @forward

@import has been officially deprecated in Dart Sass since version 1.80.0, released in October 2024. The Sass team has said it won't release Dart Sass 3.0.0, the version that removes it, until at least two years later. That means no earlier than October 2026. You can ignore the warning for now, but if you haven't rewritten your import graph by the time 3.0.0 arrives, the compiler will treat @import as a literal CSS directive instead of a Sass rule, and your build will break. This takes more than replacing one keyword across your codebase. You need to rethink how shared variables, mixins, and design tokens move through your stylesheets.

  • Deprecation status: @import has been deprecated since Dart Sass 1.80.0 (October 2024).
  • Final removal target: Dart Sass 3.0.0, no earlier than two years after 1.80.0, or October 2026.
  • @use/@forward support: available since Dart Sass 1.23.0 (2019), so you can start migrating now.
  • Migration CLI: sass-migrator (npm install -g sass-migrator, then run it with npx or the global binary).
  • Core logic: @use loads members into an isolated namespace. @forward exposes members to other files as part of an API.
  • Bundler impact: Webpack and Vite users need to update their aliases to match the new module resolution paths.
  • On node-sass: this particular deprecation won't affect you, but that doesn't mean you're in the clear. node-sass wraps the older, already deprecated LibSass engine, which never implemented the module system. If you're still using it, moving to Dart Sass should come first.

Why Is Sass @import Actually Being Deprecated?

The legacy @import rule was designed to resemble standard CSS, but it gave developers a global scope problem. Whenever you imported a file, its variables, mixins, and functions became available to everything compiled after it. Finding where a particular $brand-primary variable was defined could mean searching the entire codebase.

Library authors worked around name collisions by adding long prefixes to everything, even when those names were awkward to use.

Performance suffered too. If five components used @import "buttons", Sass could compile the button styles five times, once for each importing file. The output from a shared partial could grow with every file that loaded it. Scope problems can also cause stubborn CSS bugs, such as a box-shadow that silently refuses to render because an inherited rule higher up the cascade overrides it.

The Real Consequence: What Happens If You Don't Migrate?

It's easy to assume that leaving legacy code alone means putting up with a console warning. The consequences go further. Dart Sass is moving toward treating @import the way standard CSS does: as a browser directive for fetching an external stylesheet.

When Dart Sass 3.0 removes support for Sass's implementation of @import, the compiler will stop pulling _variables.scss or _mixins.scss partials into your main bundle. Instead, it will write a literal @import url('_variables.scss'); into the compiled CSS.

The browser will then try to fetch a .scss file that doesn't exist on your production server, and the import will fail silently. Variables, mixins, and functions that depended on that partial will no longer be available, so the declarations that use them may disappear or produce invalid CSS. Your layout can break wherever those styles mattered.

Advertisement

Many migration guides give this consequence little attention.

The Core Rule: When to Reach for @use vs @forward

The tricky part of this migration is knowing which new rule belongs in which file. Think about what the file is meant to do.

Use @use for Application Logic

Use @use when a file needs variables, mixins, or functions to produce CSS. This usually means a component stylesheet.

Loading a file with @use 'colors'; creates a namespace based on its file name. You then access variables as color: colors.$primary;. Their scope stays local to the component.

Rely on @forward for Shared Libraries

Use @forward for an index file or shared library file that passes variables and mixins on to other files. It provides a central route to those members.

If your design tokens live in fifteen files, you don't want every component to @use each one. Create a central _index.scss that forwards the individual partials instead. Files that load the index can access all the forwarded members through it.

Step-by-Step Migration and Fixing Broken Builds

Running the Automated sass-migrator

The Sass team provides an official CLI to handle much of the conversion. If npx gives you version errors, first check that your Node.js install is current. Run the tool against your main entry point so it can follow the dependency tree:

Advertisement
npx sass-migrator module --migrate-deps src/styles/main.scss

It can rewrite basic imports and add namespaces, but complex legacy codebases often need manual fixes before they build again.

Troubleshooting "Declaration Order" Errors

A common error after running the migrator involves declaration order. Under the old system, you could put an @import statement almost anywhere in a file, including inside a selector.

The module system doesn't allow that and reports an error:

Error: @use rules must be written before any other rules.

Every @use and @forward rule must be at the very top of the file, before any CSS rule, variable, or mixin definition. If the migrator can't extract a nested import, move it to the top yourself and adjust the surrounding scope.

Resolving Shared Variable Collisions

In the @import era, you could override a library's default by defining a variable before importing the library. The module system doesn't accept that implicit override and reports an error instead:

Error: This variable was not declared with !default in the @used module.

To configure a module, pass overrides explicitly with the with keyword. If the migrator doesn't preserve your theme overrides, rewrite the import like this:

@use 'library' with (
  $primary-color: #ff0000,
  $border-radius: 4px
);

A Modern Architecture: Structuring Design Tokens with @forward

To make the module system useful, give your design tokens a central API. Create a tokens directory for the raw values: _colors.scss, _typography.scss, and _spacing.scss.

Advertisement

Add an _index.scss file inside that directory. Use @forward there to bring the tokens together and prefix them so their names stay clear when other files consume them:

// tokens/_index.scss
@forward 'colors' as color-*;
@forward 'typography' as font-*;
@forward 'spacing' as space-*;

Each component then needs just one @use statement. The prefixes keep variable names readable and help prevent collisions:

// components/_button.scss
@use '../tokens';

.btn {
  background-color: tokens.$color-primary;
  padding: tokens.$space-medium;
}

Handling Path Resolution in Bundlers

Moving from @import to @use can break path resolution in bundlers such as Vite and Webpack. If you used includePaths in your Sass loader config to avoid long relative paths like ../../../styles/variables, you'll need to update that setup.

Modern Dart Sass supports the pkg: URL scheme, and bundler aliases can keep paths short. Vite's built-in alias resolution works with @use once vite.config.js points to the styling directory:

resolve: {
  alias: {
    '@styles': path.resolve(__dirname, './src/styles')
  }
}

You can then write @use '@styles/tokens'; from anywhere in the project without relying on fragile relative paths. After migrating, add the stylelint rule at-rule-disallowed-list: ['import'] to your CI pipeline so a legacy @import doesn't slip back in.

Frequently asked

Questions & answers

How do I fix the Sass @import deprecation warning?

Replace each @import with @use, then add the module namespace to variable, mixin, and function references. For example, use colors.$primary instead of $primary. Running sass-migrator module --migrate-deps on your entry file can handle much of the conversion.

What's the difference between @use and @forward in Sass?

@use loads a module's members into your file under a namespace so you can use them. @forward makes another module's members available through your file's public API. Index files use it to bring several partials together behind one import.

Does sass-migrator convert @import automatically?

It handles most straightforward cases by rewriting imports and adding namespaces. Nested imports inside selectors and shared variable overrides often need manual fixes afterward.

Why does Sass say a variable wasn't declared with !default after I switched to @use?

The variable you're trying to set with the with() clause isn't marked !default in the module, so Sass won't let you override it. Add !default to its declaration if you control the module, or check the library's docs for the right configurable variable.

How do I share SCSS variables across multiple files with @use?

Put the variables in their own partial and @use it wherever needed. For a larger set of design tokens, create an _index.scss that @forwards the partials with a shared prefix. Then each consuming file needs only one @use statement.

Share Article