← Back to postsWhat Is a Variable in Power BI? (2026 Guide)

What Is a Variable in Power BI? (2026 Guide)

Carlos GarciaCarlos Garcia9/23/2026

If you have spent any time reading DAX measures written by someone more experienced than you, you have seen the word `VAR` sitting at the top of them. It looks like it should be obvious. It is not, quite, because the word "variable" means something slightly different in DAX than it does in almost every other language you may have met it in.

This is the thing that trips people up, and it is worth saying immediately: a variable in Power BI does not vary. Once it is assigned, it holds that value for the rest of the expression. It cannot be reassigned, incremented, or looped over. It is closer to a labelled sticky note than to a box you keep putting new things into.

That one constraint is also why variables are so useful. Because the value is fixed at the moment of evaluation, you always know exactly what you are working with, which removes an entire category of bugs that DAX is otherwise very good at producing.

What is a variable in Power BI?

A variable in Power BI is a named value declared inside a DAX expression with the `VAR` keyword, calculated once in the filter context where it is declared, and then reused by name anywhere in the expression that follows it. Every measure that declares variables ends with a `RETURN` statement that says which result to actually hand back.

The minimal shape looks like this:

```

Total Margin % =

VAR TotalSales = SUM( Sales[Amount] )

VAR TotalCost = SUM( Sales[Cost] )

VAR Margin = TotalSales - TotalCost

RETURN

DIVIDE( Margin, TotalSales )

```

Three values get named, the last line does the arithmetic, and the measure reads like a sentence. Without variables the same measure would be one long nested expression that repeats `SUM( Sales[Amount] )` twice and is unpleasant to change six months from now.

Two properties matter more than anything else about them. First, a variable is evaluated exactly once, at the point of declaration. Second, it is evaluated in the filter context that exists at that point — not the context in effect later in the expression. Nearly every surprising result involving variables traces back to the second property.

Wondering whether your reporting stack is actually pulling its weight for search? Get a free SEO audit and see where your content stands.

How variables actually behave

They are immutable

You cannot write `VAR x = 1` and then later set `x = 2`. If you need a second value you declare a second variable. This feels restrictive for about an hour and then stops mattering, because DAX is a functional language and you were never going to loop anyway.

The upside is that a variable's value is knowable by reading the line that declares it. You do not have to trace execution through the rest of the measure to find out what it holds.

They are scoped to the expression that declares them

A variable declared inside a measure is invisible to every other measure. There is no such thing as a global DAX variable. If two measures need the same intermediate calculation, either duplicate the variable in both or — better — pull the calculation out into its own measure and reference that.

This is not a limitation so much as a nudge toward better modelling. When you find yourself pasting the same `VAR` into four measures, that calculation wants to be a measure of its own.

They capture filter context at the point of declaration

This is the important one. Consider a measure that tries to compare this year's sales to a filtered subset:

```

Sales vs Big Orders =

VAR AllSales = SUM( Sales[Amount] )

RETURN

CALCULATE( AllSales, Sales[Amount] > 1000 )

```

Newcomers expect the `CALCULATE` to re-filter `AllSales`. It does not. `AllSales` was already evaluated and reduced to a single number before `CALCULATE` ever ran, so the filter has nothing left to act on. The measure returns the unfiltered total.

That behaviour is not a bug. It is exactly the guarantee variables offer — evaluated once, in the context where they were written. But it means you have to decide deliberately whether you want the value frozen or recomputed. If you want it recomputed under a new filter, put the expression inside `CALCULATE` rather than the variable name.

They can hold tables, not just numbers

A variable can hold a scalar or an entire table. This is where they become genuinely powerful:

```

Top Customers Revenue =

VAR TopTen =

TOPN( 10, VALUES( Customer[Name] ), [Total Sales], DESC )

RETURN

CALCULATE( [Total Sales], TopTen )

```

Naming the table makes the intent legible. Inlining the same `TOPN` call into `CALCULATE` works identically but reads like a puzzle.

Ranking well takes more than good data. Run a free audit of your site to find the technical gaps holding your pages back.

How to use variables in a DAX measure

The mechanics are straightforward once you have written a few.

Step 1 — Open the measure editor. In Power BI Desktop, select your table in the Data pane, choose New measure from the ribbon, and the formula bar opens. Press Shift+Enter to add line breaks; a multi-line measure is far easier to read than one that runs off the right edge.

Step 2 — Declare your first variable. Type `VAR`, a space, a name, an equals sign, and the expression. Names follow the usual rules: letters, digits and underscores, no spaces, no reserved words. A name like `TotalSales` is fine; `Total Sales` is not.

Step 3 — Add as many as the calculation needs. Each `VAR` goes on its own line. Later variables can reference earlier ones, which is how you build up a calculation in readable stages rather than one nested blob.

Step 4 — Write the RETURN. Exactly one `RETURN` per expression, and it must come after all the `VAR` lines. Whatever follows `RETURN` is what the measure outputs. Forgetting it is the single most common syntax error people hit.

Step 5 — Use RETURN to debug. This is the trick worth knowing. Temporarily change the `RETURN` line to output one of your intermediate variables instead of the final result. Drop the measure into a card visual and you can see exactly what that step produced. Change it back when you are done. It is the closest thing DAX has to a breakpoint.

Step 6 — Format and check. Set the format string on the measure so percentages render as percentages, then test it across a few different slicer selections. Variables behave differently under different filter contexts, and a measure that looks right on the grand total can be wrong at the row level.

When you should reach for a variable

Use one whenever an expression appears more than once in a measure. Beyond readability, this genuinely improves performance — the engine evaluates the variable once rather than recomputing the same subexpression each time it appears.

Use one when a calculation has natural stages. A margin measure has a sales step, a cost step, and a division step. Naming those three stages turns a formula into documentation that cannot go stale, because the names are the code.

Use one when you need to freeze a value before changing filter context. This is the deliberate version of the trap described earlier: capture the current-context value in a variable, then use `CALCULATE` to compute a different-context value, then compare the two. Year-over-year and percent-of-total measures almost always follow this shape.

Use one when a table expression is getting long. `TOPN`, `FILTER`, `SUMMARIZE` and friends produce tables that are much easier to reason about when they have a name attached.

Use one when you are debugging anything. The `RETURN`-swap technique alone justifies writing measures with variables by default, even for calculations simple enough not to need them.

Not sure which pages deserve the effort? Start with a free SEO audit and work from the data instead of guesswork.

Limitations and common mistakes

The filter context trap. Covered above, and worth repeating because it accounts for most confused forum posts on the subject. If your variable seems to ignore a `CALCULATE` later in the measure, it is not ignoring it — it finished evaluating before that line ran.

No reassignment. If you are coming from Python or M, the instinct to update a value in place will surface occasionally. It does not exist here. Declare a new variable instead.

One RETURN only. You cannot branch with multiple `RETURN` statements. Conditional logic goes inside the single returned expression, usually with `IF` or `SWITCH`.

Scope is local, always. A variable declared in one measure is genuinely invisible elsewhere. There is no import, no shared scope, no workaround. Reusable logic belongs in its own measure.

Names collide with columns. Avoid naming a variable the same thing as a column or measure in your model. DAX will usually resolve it, but the ambiguity makes the code harder to read and occasionally produces a result you did not intend.

Lazy evaluation is not guaranteed to help you. The engine may skip evaluating a variable whose value is never used, which is good for performance but means you cannot rely on a variable's evaluation as a side effect. DAX has no side effects anyway, so this rarely bites — but it is worth knowing that declaring a variable is not the same as forcing it to run.

Variables do not fix a bad model. If your measures need six variables to work around a missing relationship or a table that should have been split, the variables are a symptom. Fix the model.

Variables versus the alternatives

Versus repeating the expression. Repetition is the thing variables exist to replace. It is slower, because the engine may evaluate the subexpression more than once, and it is riskier, because when the logic changes you have to remember every place it appears. There is no case where repeating a complex expression three times beats naming it once.

Versus calculated columns. A calculated column is computed at refresh time and stored in the model, consuming memory and ignoring whatever filters the user applies at report time. A variable is computed at query time inside the current filter context. If the value needs to respond to slicers, it belongs in a measure with variables, not in a column. Calculated columns make sense for static attributes — a category grouping, a date flag — not for anything a user will filter.

Versus separate measures. A measure is reusable across the whole model; a variable is not. If more than one measure needs the intermediate value, make it a measure. If only one does, a variable keeps the logic local and avoids cluttering the field list with helper measures nobody should click on.

Versus Power Query steps. Power Query transforms data before it lands in the model, which is the right place for reshaping, cleaning, and joining. DAX variables operate on data that is already loaded, in response to what the user is looking at right now. Heavy row-by-row transformation belongs upstream in Power Query; context-dependent aggregation belongs downstream in DAX.

Final Thoughts

A variable in Power BI is a small idea with an outsized effect on the measures you write. It names a value, computes it once, and freezes it in the filter context where it was declared. That is the whole feature. The reason experienced DAX authors use them constantly is not that complex measures require them, but that measures written with variables are readable six months later and measures written without them frequently are not.

If you take one habit away, make it the debugging one. Write your measures in stages, and when a result looks wrong, change the `RETURN` line to expose an intermediate variable and see which stage broke. That single technique will save you more time than any amount of reading about evaluation contexts.

Once your measures are solid, the next question is usually whether the reports built on them are actually being found. If you are working across the Microsoft stack, our guide to writing DAX functions in Power BI is the natural next step, and it assumes exactly the variable knowledge covered here.

Ready to find out what is holding your site back? Claim your free SEO audit and get a clear list of fixes.