Skip to content

Fix: Sass Error - Undefined Variable

FixDevs ·

Quick Answer

Fix the Sass undefined variable error caused by @use vs @import migration, variable scope issues, and dart-sass namespace changes with clear solutions.

The Error

You compile your Sass files and get:

Error: Undefined variable.
   |
12 | color: $primary-color;
   |        ^^^^^^^^^^^^^^
  src/components/button.scss 12:8  @use

Or with older node-sass:

SassError: Undefined variable: "$primary-color"
        on line 12 of src/components/button.scss

The variable exists in another file, but Sass can’t find it during compilation.

Why This Happens

Sass variable visibility depends on how you import files. The modern @use rule (introduced in Dart Sass) creates a namespace for each imported file. Variables from @use-imported files aren’t available globally — you must access them through their namespace. This is the most common cause of the error when migrating from @import to @use.

With the legacy @import rule, all variables were global. With @use, they’re scoped. This is intentional — it prevents naming collisions — but it breaks existing code that assumes global access.

Fix 1: Use the Correct Namespace

When you @use a file, its variables are available under a namespace matching the filename:

// _variables.scss
$primary-color: #3b82f6;
$font-size-base: 16px;

// button.scss
@use 'variables';

.button {
  // Wrong - no namespace
  color: $primary-color;

  // Correct - with namespace
  color: variables.$primary-color;
  font-size: variables.$font-size-base;
}

The namespace is the last component of the file path without the extension. For @use 'config/variables', the namespace is variables.

Customize the namespace with as:

@use 'variables' as vars;

.button {
  color: vars.$primary-color;
}

Or remove the namespace entirely with as *:

@use 'variables' as *;

.button {
  color: $primary-color; // Works without namespace
}

Common Mistake: Overusing as * defeats the purpose of @use namespacing. Reserve it for files you use extensively (like your design tokens). For everything else, keep the namespace to avoid collisions.

Fix 2: Configure @forward for Library Files

If you have an index file that re-exports variables from multiple files, use @forward:

// abstracts/_variables.scss
$primary-color: #3b82f6;

// abstracts/_mixins.scss
@mixin flex-center { display: flex; align-items: center; justify-content: center; }

// abstracts/_index.scss
@forward 'variables';
@forward 'mixins';

// components/button.scss
@use '../abstracts';

.button {
  color: abstracts.$primary-color;
  @include abstracts.flex-center;
}

@forward makes the forwarded module’s members available to anyone who @uses the forwarding file. Without @forward, @use 'abstracts' would only expose members defined directly in _index.scss.

Fix 3: Fix File Import Order

Variables must be defined before they’re used. With @use, the order matters:

// Wrong - using variable before it's available
@use 'components/button'; // Uses $primary-color
@use 'variables'; // Defines $primary-color

// Correct - define first, use second
@use 'variables';
@use 'components/button';

However, each file should @use its own dependencies rather than relying on import order in a main file:

// components/_button.scss — self-contained
@use '../variables';

.button {
  color: variables.$primary-color;
}

This makes each file independent and eliminates order-related issues.

Fix 4: Fix Partial File Naming

Sass partials must start with an underscore. Without it, Sass may not find the file:

styles/
├── _variables.scss    ✓ Partial (imported, not compiled alone)
├── variables.scss     ✗ Compiled as standalone file
└── main.scss

When you write @use 'variables', Sass looks for _variables.scss. If the file is named variables.scss without the underscore, Sass treats it as a standalone file, not an importable partial.

Rename your files to include the underscore prefix:

mv variables.scss _variables.scss
mv mixins.scss _mixins.scss

Fix 5: Migrate from node-sass to Dart Sass

node-sass is deprecated and doesn’t support @use or @forward. If you’re using @import with node-sass, everything is global. But if you switch to Dart Sass without updating your import style, variables break:

# Check which Sass you're using
npx sass --version  # Dart Sass
node -e "console.log(require('node-sass').info)"  # node-sass

Migrate to Dart Sass:

npm uninstall node-sass
npm install sass

After switching, you have two options:

  1. Keep using @import (works but deprecated, will be removed in Dart Sass 3.0)
  2. Migrate to @use/@forward (recommended)

Use the official migration tool:

npx sass-migrator module style.scss

Pro Tip: The Sass Migrator tool handles most of the @import to @use conversion automatically, including adding namespaces and converting variable references. Run it with --dry-run first to preview changes.

Fix 6: Fix Load Paths

If your Sass files are in a non-standard directory, Sass may not find them. Configure load paths:

# CLI
sass --load-path=src/styles main.scss output.css

# Webpack (sass-loader)
{
  loader: 'sass-loader',
  options: {
    sassOptions: {
      includePaths: [path.resolve(__dirname, 'src/styles')]
    }
  }
}

For Vite:

// vite.config.js
export default {
  css: {
    preprocessorOptions: {
      scss: {
        includePaths: ['src/styles']
      }
    }
  }
}

With load paths configured, you can use shorter imports:

// Instead of
@use '../../styles/variables';

// You can write
@use 'variables';

Fix 7: Fix Variable Scope in Control Structures

Variables defined inside control structures (@if, @each, @for) are local to that block:

// Wrong - $color is scoped to the @if block
@if $theme == 'dark' {
  $color: #ffffff;
}
.text { color: $color; } // Error: Undefined variable

// Correct - declare outside, assign inside
$color: #000000;
@if $theme == 'dark' {
  $color: #ffffff !global;
}
.text { color: $color; }

The !global flag is needed to modify a variable from an outer scope inside a control structure. Without it, Sass creates a new local variable.

For mixin arguments, variables are scoped to the mixin body:

@mixin theme($bg, $text) {
  background: $bg;
  color: $text;
  // $bg and $text don't exist outside this mixin
}

Fix 8: Configure Global Variables with @forward ... with

Sometimes you need to override default variables from a library. Use @forward ... with to set configurable defaults:

// _config.scss
$primary-color: #3b82f6 !default;
$border-radius: 4px !default;

// _index.scss
@forward 'config';

// main.scss — override defaults
@use 'index' with (
  $primary-color: #ef4444,
  $border-radius: 8px
);

Variables must be marked with !default to be configurable. The with clause only works with @use, not @forward.

For third-party libraries like Bootstrap:

// Override Bootstrap variables before importing
@use 'bootstrap/scss/bootstrap' with (
  $primary: #custom-color,
  $enable-shadows: true
);

Still Not Working?

  • Check for typos in variable names. Sass variables are case-sensitive. $Primary-Color and $primary-color are different variables.

  • Verify file extensions. Sass processes .scss and .sass differently. Don’t mix syntaxes in @use without specifying the extension.

  • Clear your build cache. Stale cached files can cause phantom errors. Delete .sass-cache/, node_modules/.cache/, and your output directory.

  • Check for circular imports. If file A uses file B and file B uses file A, Sass may fail silently. Restructure to break the cycle — extract shared variables into a separate file.

  • Verify your bundler configuration. Webpack, Vite, and other bundlers resolve Sass imports differently. Check that your sass-loader or preprocessor options match your file structure.

  • Test with the Sass CLI directly. Run npx sass input.scss output.css to isolate whether the issue is with Sass itself or your build tool’s configuration.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles