🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA Function in Excel — Return Values & Custom Worksheet Functions (UDF)

|

VBA Function in Excel — Return Values & Custom Worksheet Functions (UDF)

Tested on: Excel 365 v2509 · Excel 2021 · Excel 2019 · last verified 2026-06-07

TL;DR — A Function is a procedure that hands a value back. You return that value by assigning it to the function's own name — there is no Return keyword in VBA. And because a Function returns a value, you can type it straight into a worksheet cell as a custom formula (a UDF):

Function AddTax(price As Double, rate As Double) As Double
    AddTax = price * (1 + rate)     ' assign to the function's NAME — this is the return
End Function
In any cell:   =AddTax(A2, 0.2)     ' your own formula, sitting next to SUM and VLOOKUP

That one capability — living inside a cell — is the entire reason Functions exist alongside Subs. Everything below builds on it.

The mental model: a Function is a Sub that reports back

A Sub is a worker who does a task and walks away. A Function is a worker who does a task and then tells you the answer. Same machinery — a named block of code, arguments in — with one addition: a value comes out.

That output is what makes a Function usable where a Sub never can be: inside a worksheet cell. Excel's grid is built to display the result of an expression, so anything that returns a value — SUM, XLOOKUP, or your AddTax — can sit in a cell. A Sub returns nothing, so Excel has nothing to put in the cell; type =GreetUser() and you get #NAME?. Hold onto that picture and the Sub-vs-Function decision answers itself.

The one rule: return by assigning to the function's name (not Return)

This is the bug that catches everyone coming from Python, JavaScript, or C#:

Function Discount(price As Double) As Double
    Discount = price * 0.9      ' ✅ VBA returns by assigning to the function's own name
End Function

Function Broken(price As Double) As Double
    Return price * 0.9          ' ❌ not how VBA works — silently returns 0
End Function

Return exists in VBA, but only as the partner of the ancient GoSub — it does not return a value. If you write Return (or simply forget to assign the name at all), the function compiles fine and quietly returns the default for its type — 0, "", or Empty. No error, just wrong answers. If a UDF keeps returning 0, you almost certainly forgot to assign to its name.

You can assign the name as many times as you like (it's a normal variable inside the function); the value it holds when the function ends is what comes back:

Function Grade(score As Long) As String
    Grade = "Fail"              ' default
    If score >= 50 Then Grade = "Pass"
    If score >= 80 Then Grade = "Distinction"
End Function

Sub vs Function — the one-line decision

Forget the long comparison tables. The whole choice is one question:

Does the caller need an answer back? Yes → Function. No → Sub.

Sub Function
Returns a value No Yes (assign to its name)
Usable in a worksheet cell No Yes (it's a UDF)
Shows in the Macros dialog (Alt+F8) Yes, if Public & no args No
Typical job Do something (format, export, delete) Calculate something (a rate, a label, a cleaned string)

A useful tell: if you find yourself making a Sub stuff its result into a global variable so another procedure can read it, you wanted a Function. Return the value properly instead of smuggling it through a global.

Make it a worksheet function (UDF) — the killer feature

Put the Function in a standard module (Insert → Module, not a sheet or ThisWorkbook module), and it instantly becomes available in the grid and in the formula AutoComplete:

Function InitialsOf(fullName As String) As String
    Dim parts() As String
    parts = Split(Trim(fullName), " ")
    InitialsOf = Left(parts(0), 1) & Left(parts(UBound(parts)), 1)
End Function
=InitialsOf("Ada Lovelace")  →  "AL"

This is where VBA pays off for non-programmers around you: you write the logic once, and your colleagues use it like any built-in function, no code in sight.

The rule that breaks every new UDF: a worksheet function can only return to its own cell

Here is the limitation that generates a thousand forum posts. When a Function is called from a cell, Excel runs it in a protected mode where it cannot change anything — it can't write to other cells, can't set a colour, can't rename a sheet, can't MsgBox. It may only compute and return the value for the cell that called it.

Function ColorMe(c As Range) As String
    c.Interior.Color = vbYellow   ' ❌ silently does NOTHING when called from a cell
    ColorMe = "done"
End Function

People write this, see the text appear but no colour, and conclude VBA is broken. It isn't — UDFs are pure by design. If you need to change the sheet, that's a job for a Sub (triggered by a button or an event), not a UDF. Knowing this line saves hours: cell formula → compute and return only; change the workbook → use a Sub.

Optional and typed arguments make a UDF feel native

Two touches make a custom function pleasant to use:

Function Net(gross As Double, Optional rate As Double = 0.2) As Double
    Net = gross / (1 + rate)       ' rate defaults to 0.2 if the caller omits it
End Function

Optional with a default lets =Net(A2) and =Net(A2, 0.19) both work. Declaring the return type (As Double, As String, As Boolean) instead of leaving it Variant makes the function faster and the intent obvious.

The opinion: don't reinvent a built-in

VBA Functions are for logic Excel doesn't already have. If WorksheetFunction.SumIf, TEXTJOIN, or XLOOKUP already does the job, call that — a native function is faster, recalculates correctly, and needs no macro-enabled workbook. Reserve your own Functions for genuinely custom rules: a company-specific tax band, a messy-string parser, a domain calculation no built-in covers. A UDF that wraps SUM is a liability; a UDF that encodes your business rule is an asset.

Common Function mistakes (and the fix)

Symptom Cause Fix
Function always returns 0 / blank Used Return, or never assigned the name Assign the result to the function's own name: MyFunc = result
#NAME? in the cell Function is in a sheet module, or the name is misspelled Move it to a standard Module; check spelling
UDF won't colour/change cells A cell-called UDF can't alter the workbook Use a Sub on a button/event for changes
#VALUE! in the cell An unhandled error inside the function Validate inputs; return a fallback instead of erroring
UDF result won't refresh Function doesn't see the changed input Reference the changed cells as arguments, or add Application.Volatile (sparingly)
"Expected: end of statement" calling it Bracket/Call confusion (same as Subs) Use the return value: x = MyFunc(a)

When the formula logic is the real work — skip the VBA

A custom Function is the right tool for one reusable calculation. But when the task is "reconcile these two exports, flag the rows that don't match, and total the differences by month," you don't want to hand-write and maintain that. ExcelMaster Agent takes that sentence in plain English and produces the result — no UDF to debug, no macro-enabled file to ship. Try it free →

FAQ

What is a Function in VBA? A Function is a named procedure that performs work and returns a value to the code that called it. You define it with Function Name(args) As Type … End Function and return the result by assigning it to the function's own name.

How do I return a value from a VBA function? Assign the value to the function's name: inside Function Total() As Double, write Total = 42. VBA has no Return statement for this — Return belongs to the old GoSub and does not pass a value back. The last value assigned to the name before the function ends is what gets returned.

Why does my VBA function return 0 or blank? Because you never assigned a value to the function's name (or you used Return, which doesn't work in VBA). The function then returns the default for its declared type — 0, "", or Empty. Add FunctionName = result before End Function.

How do I create a custom function (UDF) in Excel? Put a Function in a standard module (Insert → Module), give it a return type, and assign the result to its name. Then type =YourFunction(...) in any cell. It appears in formula AutoComplete like a built-in function.

Why can't my UDF change other cells or colours? When Excel calls a Function from a worksheet cell, it runs in a restricted mode that can only return a value — it cannot modify the workbook (no writing to cells, formatting, or MsgBox). To change the sheet, use a Sub triggered by a button or event instead of a UDF.

When should I use a Function instead of a Sub? Use a Function when the caller needs a value back — a calculation, a lookup, a cleaned string — or when you want to call it from a worksheet cell. Use a Sub when the procedure only does something (formats, exports, deletes) and returns nothing.