Why Your SCSS Variable Is Undefined and How to Fix It

Quick answer

💡SCSS variable undefined errors almost always mean the variable is defined in a different file and not properly imported with @use, or it is defined inside a rule block and therefore locally scoped. Switch from @import to @use with the correct namespace, or move the variable declaration to the top level of a partial file.

Error symptoms

  • Undefined variable: $primary-color in your SCSS compile output
  • Property value expected error at the line referencing a variable
  • Unknown word error when a variable is used outside its declaring file
  • Styles compile fine locally but fail in CI because Dart Sass is stricter
  • Variable resolves to an empty string instead of its expected value
  • Sass deprecation warnings about @import that eventually become hard errors

Common causes

  • Using @import instead of @use — @import is deprecated and behaves differently with namespacing
  • Forgetting the namespace prefix: @use 'variables' creates a variables. namespace, not a global one
  • Declaring a variable inside a selector rule instead of at the top level of the file
  • Missing underscore prefix on partial files that prevents them from being found by @use
  • Circular @use or @forward chains where the variable is not yet resolved when referenced
  • Using !default incorrectly — @use loads the module once, so overrides must come before @use

When it happens

  • After migrating a codebase from @import to @use without updating all namespace references
  • When splitting a monolithic SCSS file into partials for the first time
  • When adding Dart Sass to a project that previously used Node Sass or LibSass
  • After moving a variable declaration into a mixin or media query block

Examples and fixes

The most common cause — using a variable from another file without the @use namespace.

Accessing a variable across files with @use

❌ Wrong

// _theme.scss
$primary: #3b82f6;
$radius: 4px;

// button.scss
@import 'theme';

.button {
  background: $primary;
  border-radius: $radius;
  color: white;
  padding: 0.5rem 1rem;
}

✅ Fixed

// _theme.scss
$primary: #3b82f6;
$radius: 4px;

// button.scss
@use 'theme';

.button {
  background: theme.$primary;
  border-radius: theme.$radius;
  color: white;
  padding: 0.5rem 1rem;
}

When you switch from @import to @use, the file's top-level variables become namespaced under the filename by default. You must reference them as filename.$variable. If the namespace feels verbose, you can alias it with @use 'theme' as t and then use t.$primary, or use @use 'theme' as * to pull all variables into the global namespace — though the wildcard approach is discouraged in large codebases because it makes it hard to trace where a variable originates.

Declaring a variable inside a rule makes it locally scoped and invisible to sibling or child rules.

Variable defined inside a selector instead of at file level

❌ Wrong

// _card.scss
.card {
  $card-padding: 1.5rem;
  $card-bg: #f8fafc;
}

.card-body {
  padding: $card-padding;
  background: $card-bg;
  border-radius: 8px;
  font-size: 0.875rem;
}

✅ Fixed

// _card.scss
$card-padding: 1.5rem;
$card-bg: #f8fafc;

.card {
  padding: $card-padding;
  background: $card-bg;
}

.card-body {
  padding: $card-padding;
  background: $card-bg;
  border-radius: 8px;
  font-size: 0.875rem;
}

Variables declared inside a CSS rule in SCSS are locally scoped to that block. They cannot be accessed by sibling rules or rules at other levels of the file, even in the same document. Move all design token variables to the top of the file or into a dedicated partial like _variables.scss or _tokens.scss. This makes them accessible anywhere that file is included with @use.

Why @use Namespacing Causes Undefined Errors

Dart Sass introduced @use and @forward as replacements for the long-deprecated @import rule. The fundamental difference is that @use does not pollute the global namespace. When you write @use 'variables', Sass makes that file's contents available under the variables. namespace. Every variable, mixin, and function from that file must be accessed as variables.$my-var rather than just $my-var. This is intentional — it makes it immediately clear which file a value comes from.

The deprecation of @import happened gradually. LibSass and older versions of node-sass still supported @import without warnings, so many codebases continued using it. When a project upgrades to Dart Sass 1.23 or later (which ships with modern bundlers like Vite, webpack 5, and Next.js), the existing @import statements generate deprecation warnings, and in Dart Sass 2.x they will become hard errors.

The most common migration mistake is adding @use without updating the variable references. A developer changes @import 'variables' to @use 'variables' and immediately gets undefined variable errors on every $primary, $spacing-*, and $font-* reference in the file. The fix is to either add the namespace prefix to each reference or to use the wildcard @use 'variables' as * — but the wildcard should be reserved for small files where namespace clarity is less important.

There is a second, subtler issue with @use: a file can only be used once per module graph. If two files both @use 'theme', Dart Sass loads the module once and shares it. This is efficient but it means you cannot use !default overrides in two different consuming files. Overrides must happen before any @use statement, typically in an entry file using @use 'theme' with ($primary: red).

Diagnosing the Error in DevTools and CLI

Start with the compiler output. Dart Sass prints the file path and line number where the undefined variable was referenced, as well as any stack frames showing which @use or @forward chain led to that point. The error message will say something like: Error: Undefined variable. with the variable name and the line number. Read the message carefully — it tells you exactly where the reference failed, not where the variable should be defined.

Open your browser's DevTools (F12) and check the Sources panel or the CSS pane. If Sass source maps are enabled, clicking on a style in the Elements panel will take you to the original .scss file and line. This is helpful for confirming that a compiled stylesheet is missing a rule entirely — which happens when a variable is undefined and the entire block fails to compile.

At the terminal, run your Sass CLI with verbose output. For Dart Sass: sass --watch src/styles:public/css --no-source-map. The output will include the load order of @use statements, which lets you trace which files are being resolved and in what order. If a file is listed twice in the stack trace under @use, there may be a circular dependency.

For larger projects, search your codebase for the failing variable name. Confirm it is defined at the top level (not inside a selector), that the file containing it is a partial (filename starts with underscore), and that the consuming file uses @use 'filename' with the correct relative path. Many projects use a barrel file like _index.scss with @forward rules to re-export variables from multiple partials — verify your entry file uses that barrel.

Step-by-Step Fixes for Each Root Cause

If the error is a missing namespace: add the filename prefix to every variable reference. If you had @import 'variables' and referenced $primary, change the import to @use 'variables' and every reference to variables.$primary. Use your editor's find-and-replace to do this systematically across the file rather than hunting for each one manually.

If the variable is defined inside a selector block: cut the declaration and paste it at the top of the file, before any selector. Variables at file level are accessible from any selector in that same file. If other files also need the variable, move it to a dedicated partial like _tokens.scss and add @use 'tokens' to each consuming file.

If you want to avoid namespacing entirely — common when migrating a large codebase — use @use 'variables' as * in each file. This makes all variables from that module available without a prefix, mimicking the old @import behavior. It is not recommended for new code because it obscures where values come from, but it is a valid short-term migration strategy.

If the issue is with !default overrides: configuration must happen in a single dedicated entry file. Create a _config.scss that defines your override values, then use @use 'variables' with ($primary: $my-override-primary) in that config file. Other files should @use 'config' rather than @use 'variables' directly. This ensures overrides are applied before any module resolves its defaults.

After any fix, recompile the full stylesheet rather than just the changed file. In watch mode, Dart Sass may cache a stale module state. Kill the watch process and restart it to force a clean compile.

Tricky Edge Cases in Partial Files

Partial files must start with an underscore: _variables.scss, _mixins.scss, _tokens.scss. When you write @use 'variables', Sass resolves it to _variables.scss in the same directory. If you accidentally name your file variables.scss without the underscore, Sass will attempt to compile it as a standalone output file and may fail to resolve it as a module, or will compile it to an unwanted variables.css file.

The @forward rule is commonly misunderstood. @forward makes the contents of a module available to any file that @uses the file containing the @forward. It does not make them available inside the file itself. A barrel file _index.scss might contain @forward 'variables'; @forward 'mixins'; — consuming files then @use 'styles/index' and get access to everything forwarded. If you try to use a forwarded variable inside the barrel file itself, you will get an undefined error.

Mixins and functions that reference variables resolve at definition time, not call time. A mixin defined in _mixins.scss that references a variable from _variables.scss must @use 'variables' itself. It cannot rely on the calling file to have already loaded those variables. This is a common mistake when converting a flat @import-based stylesheet into a modular @use architecture.

If you are using Tailwind alongside SCSS, note that PostCSS and Dart Sass are entirely separate compilation pipelines. Variables defined in SCSS are not available inside Tailwind's @apply rules, and CSS custom properties defined at runtime are not the same as SCSS compile-time variables. They serve different purposes and cannot be used interchangeably across pipelines.

Mistakes Developers Make When Migrating to @use

The single biggest migration mistake is doing a bulk find-and-replace of @import with @use without also updating variable references. The files will load without syntax errors, but every variable reference will fail at runtime with an undefined error because the namespace prefix is missing. Always update the references in the same step as the import statement.

Another common mistake is using @use after other rules in a file. The @use rule must appear before any other statements except @charset. Dart Sass enforces this strictly. If you have a @mixin definition or a CSS rule before your @use statement, you will get an error about @use not being allowed after other rules. The fix is to move all @use and @forward declarations to the very top of the file.

Developers sometimes try to use @use inside a mixin or a conditional. This is not allowed — @use is a static declaration and must be at the top level of the file. If a mixin needs access to variables from another file, add @use at the top of the file that contains the mixin, not inside the mixin body.

In React and Vue projects with CSS Modules, each .module.scss file is compiled independently. A variable defined in one module is not available in another without @use. A common mistake is defining $brand-color in a component stylesheet and expecting it to be available in a sibling component. Always extract shared variables to a partial and @use it explicitly in each component that needs it.

SCSS Architecture That Prevents This Error

Structure your SCSS around a dedicated design token file. Name it _tokens.scss or _variables.scss and place it at the root of your styles directory. Define every color, spacing value, font size, border radius, and z-index here. Every other file that needs these values adds @use '../tokens' as t at the top. This creates a single source of truth and makes it trivially easy to trace where any value comes from.

Use a barrel file to simplify imports in large projects. Create _index.scss in each directory and populate it with @forward rules pointing to every partial in that directory. Consuming files then need only one @use statement per directory rather than one per file. Keep barrel files free of variable declarations and rule blocks — their only job is forwarding.

Add Stylelint with the stylelint-scss plugin to your project. Configure it to warn on @import usage and to enforce consistent namespace usage. Running Stylelint in CI catches undefined variable errors before they reach production. The no-global-function-names rule and the at-use-no-unnamespaced rule are especially useful during @import to @use migrations.

Document your namespace conventions in the project README. If your team decides to use @use 'tokens' as t everywhere, note that decision explicitly. Inconsistent aliasing — some files using @use 'tokens' as t, others using @use 'tokens' as tok, and others using @use 'tokens' as * — makes the codebase harder to navigate and increases the chance of future undefined variable errors.

For teams adopting a formal SCSS architecture, the 7-1 pattern is a proven structure that organises partials into seven folders (abstracts, base, components, layout, pages, themes, vendors) with a single main.scss entry file that forwards them all in a defined order. This structure makes variable scope and import order predictable because each category of partial has a designated folder and loading sequence, eliminating the ambiguity about which partial should @forward which variables and ensuring tokens are always available before the rules that consume them.

Quick fix checklist

  • Confirm the variable is declared at the top level of a partial, not inside a selector block
  • Check that the partial filename starts with an underscore, e.g. _variables.scss
  • Verify you are using @use instead of @import for all cross-file variable access
  • Add the namespace prefix to every variable reference: filename.$variable or alias.$variable
  • Move all @use and @forward statements to the very top of each file, before any rules
  • For !default overrides, pass them at @use time with @use 'lib' with ($var: value)
  • Restart your Sass watch process after structural changes to force a clean module cache
  • Run sass --version in both local and CI environments to confirm matching Dart Sass versions

Related guides

Frequently asked questions

What is the difference between @use and @import in SCSS?

@import makes all variables, mixins, and functions from the imported file globally available without any prefix. @use loads the module once and makes its contents available under a namespace derived from the filename. The global availability of @import causes naming collisions in large projects; @use's explicit namespacing prevents this. @import is deprecated in Dart Sass and will be removed in a future major version, so migrating to @use is strongly recommended for all new and existing projects.

How do I use a variable from another SCSS file without a namespace?

Write @use 'filename' as * to pull all variables, mixins, and functions into the current file's global scope, making them usable without a prefix. This mimics the old @import behavior. It is acceptable for small projects or during migrations but is discouraged at scale because it becomes difficult to trace which file any given variable originated from. The recommended approach for large codebases is to use explicit namespaces or short aliases.

Why does the variable work locally but fail in CI?

Local environments often have older versions of node-sass or a globally installed Sass CLI that still handles @import without error. CI pipelines frequently install the latest Dart Sass, which enforces stricter rules around @use, @import deprecation, and load order. Check your package.json to ensure sass is pinned to a specific version and that the same version is used in both local development and CI. Run sass --version locally and in CI to confirm they match.

Can I define SCSS variables inside a mixin?

Yes, but they will only be available inside that mixin's body. Variables declared inside a mixin are locally scoped to that mixin call. They cannot be accessed outside the mixin and they do not persist between mixin calls. If you need a value that is shared between a mixin and the rules that call it, define the variable at the top level of a partial file and @use that partial in both the mixin's file and any file that needs the value.

What does !default do and why is it not working?

The !default flag sets a variable's value only if it has not already been assigned. It is used to make SCSS libraries configurable — the library defines $primary: blue !default, and consuming code can override it before @using the library. The common issue is trying to override a !default variable after the module has already been loaded. Overrides must be passed at @use time using @use 'library' with ($primary: red), not by reassigning the variable after the @use statement.

How do partial files work in SCSS?

Partials are SCSS files whose names begin with an underscore, such as _variables.scss. The underscore signals to the Sass compiler that the file should not be compiled to a standalone CSS file — it is meant to be used as a module by other files. When you write @use 'variables', Sass automatically resolves this to _variables.scss in the same directory. Files without the underscore are treated as entry points and compiled to their own CSS output files.

Is SCSS @use compatible with webpack and Vite?

Yes. Both webpack (via sass-loader) and Vite (via vite-plugin-scss or built-in SCSS support) use Dart Sass under the hood and fully support @use and @forward. Configure the loadPaths option in your bundler's Sass options to allow @use statements with paths relative to a root directory rather than requiring long relative paths like @use '../../../tokens'. This is especially useful for component libraries and monorepos.

How do I share SCSS variables between multiple component files?

Create a dedicated partial file for your design tokens, such as src/styles/_tokens.scss. In each component stylesheet that needs access, add @use '@/styles/tokens' as t at the top (adjusting the path for your project structure). Reference variables as t.$variable-name. Configure your bundler's Sass loadPaths to make the @use path cleaner. Never copy variables between files — a single source of truth prevents drift and makes global changes trivial.

All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-06.