DevLearningTools

MODULE 9 · LESSON 02

Custom Tags

Building your own reusable CFML tags — the cf_ naming convention, the Attributes scope for data going in, and the Caller scope for data going back out.

New lessons are added one at a time as the course gets built out — a graded quiz for each lesson is still on the way.

A custom tag is a reusable block of CFML that behaves like a built-in tag — it runs in its own isolated scope (unlike <cfinclude>, which shares the calling page's scope entirely), and it can accept parameters and hand data back, closer to how a function works.

Learning Objectives

After completing this lesson, you'll be able to:

  • Create a custom tag and call it with the cf_ prefix.
  • Pass data into a custom tag through the Attributes scope, and back out through the Caller scope.

Creating One

A custom tag is just a regular CFML file. Save it, and it becomes callable as a tag by prefixing its filename with cf_.

CFScript — today.cfm
<cfoutput>#dateFormat(now(), "mmmm d, yyyy")#</cfoutput>
Calling it
Simulated output — illustrative only, not a live ColdFusion/Lucee server.
NOTE

Custom tag filenames must be lowercase on non-Windows systems, even though CFML tag names themselves aren't case-sensitive.

Where the File Has to Live

  • The same directory as the page calling it
  • The cfusion/CustomTags directory (or a subdirectory of it)
  • Any additional directory configured in the ColdFusion Administrator

Passing Data In: the Attributes Scope

CFScript — greet.cfm
<cfoutput>Hello, #attributes.name#!</cfoutput>
Calling it
Simulated output — illustrative only, not a live ColdFusion/Lucee server.
NOTE

Everything passed as a tag attribute lands in the custom tag's own Attributes scope, exactly like Arguments works for a function call.

Passing Data Back: the Caller Scope

To hand a result back to whichever page called the tag, write it into the Caller scope — the calling page's own Variables scope, from the custom tag's perspective.

CFScript — getGreeting.cfm
<cfscript>
caller.greetingMessage = "Hello, " & attributes.name & "!";
</cfscript>
Calling it
Simulated output — illustrative only, not a live ColdFusion/Lucee server.
NOTE

Use a distinctive name when writing to Caller, the same reasoning as scoping variables carefully anywhere else — an overly generic name risks silently overwriting something the calling page already had.

cfinclude vs Custom Tags

cfincludeCustom Tag
ScopeShares the calling page's scope entirelyRuns in its own isolated scope
Passing data inNot supported — set a variable beforehandAttributes scope, like real parameters
Passing data backAutomatic (shared scope)Explicit, via the Caller scope
Best forStatic or mostly-static shared contentReusable logic that needs its own inputs/outputs

Common Beginner Mistakes

Forgetting the cf_ prefix when calling a custom tag

Without it, ColdFusion has no way to know the tag isn't one of its own built-in tags — the cfimport lesson covers an alternative that avoids the prefix requirement entirely.

Reading a value from Attributes that wasn't actually passed

Check structKeyExists(attributes, "name") first if an attribute is optional, the same as checking any other scope before reading from it.

Assuming a custom tag can read the calling page's variables directly

It can't — that's the whole point of the isolated scope. Anything the tag needs has to come in through Attributes explicitly.

Best Practices

  • Reach for a custom tag (or a CFC) instead of cfinclude whenever the reusable block genuinely needs its own inputs or should be protected from the calling page's variables.
  • Use a distinctive Caller variable name to avoid silently overwriting something in the calling page.

Interview Questions

What's the difference between how cfinclude and a custom tag handle scope?

cfinclude shares the calling page's Variables scope entirely. A custom tag runs in its own isolated scope, receiving data only through Attributes and returning data only through Caller.

How do you pass data into a custom tag?

As tag attributes, which the tag reads from its own Attributes scope — the same pattern as a function's Arguments scope.

Summary

In this lesson, you created and called a custom tag, and passed data in through Attributes and back out through Caller.

What's Next?

The next lesson covers cfimport — calling a whole directory of custom tags under a prefix of your own choosing, instead of the cf_ convention.

MORE IN REUSABLE CODE