Definition
A ColdFusion custom tag is a reusable CFML file that you invoke like a tag to encapsulate logic or presentation. In simple terms, it’s a .cfm template that behaves like a function with attributes, can render output, and can optionally wrap body content. You call it using a tag-style Syntax (for example, <cf_myTag …> or via cfmodule), pass attributes to it, and the tag executes its code and optionally returns output or data to the caller. It’s a core way to build a custom tag library in ColdFusion/CFML for consistent, modular development.
How It Works
The basic idea
- You create a CFML file (for example, showTable.cfm).
- You call it like a tag:
. - The custom tag receives your inputs through the special ATTRIBUTES scope and can send data back via the CALLER scope or by writing output.
ColdFusion looks for the tag file in:
- The same directory as the calling page
- Any directories configured in this.customTagPaths in Application.cfc
- The server’s Custom Tag Paths (ColdFusion Administrator)
- A directory imported via cfimport taglib
Ways to invoke a custom tag
- Tag-style:
… - Module-style:
- Namespaced (via cfimport): <ui:myTag …> after cfimport taglib=”/path/to/tags” prefix=”ui”
Anatomy of a Custom Tag
Inside a custom tag template (the .cfm file), you use a few special scopes and conventions:
- ATTRIBUTES: A struct of all attributes passed to the tag. Example: attributes.title
- VARIABLES: The tag’s private scope for local variables; safe from the caller’s variables.
- CALLER: The calling page’s scope; use it to return values (carefully).
- THIS.TAG (or ThisTag): A struct with runtime info about the tag, such as:
- ThisTag.ExecutionMode: “start” or “end”
- ThisTag.HasEndTag: true/false
- ThisTag.GeneratedContent: content between start and end tags
Common patterns:
- Validate inputs with cfparam on attributes
- Render or capture output
- Optionally put results into caller scope
Passing Attributes and Default values
- Pass attributes just like any CFML tag:
- Read attributes in the tag file using the ATTRIBUTES scope.
- Provide defaults and type checks using cfparam:
-
You can also pass dynamic sets of attributes using attributeCollection with cfmodule or namespaced tags:
Execution Modes and Body Content
Custom tags can be used with or without an end tag:
- Self-closing:
- Paired:
… inner HTML or CFML …
When you use a start and end tag, ColdFusion runs the tag twice:
- Start run (ThisTag.ExecutionMode = “start”)
- End run (ThisTag.ExecutionMode = “end”), where ThisTag.GeneratedContent contains the inner content
This makes it easy to wrap markup around body content.
Syntax and Examples
Example 1: A simple outputting tag
File: /customtags/formatDate.cfm
-
-
- <cfset variables.out = dateFormat(attributes.date, attributes.format)>
-
#variables.out#
Usage:
Example 2: A wrapping tag that uses GeneratedContent
File: /customtags/panel.cfm
-
- <cfif ThisTag.ExecutionMode EQ “start”>
- <cfelseif ThisTag.ExecutionMode EQ “end”>
-
#encodeForHtml(attributes.title)#
#ThisTag.GeneratedContent#
-
Usage:
-
–#dateFormat(now(), “mmm d, yyyy”)#
–- Order #1
- Order #2
Example 3: Returning values to the caller
File: /customtags/makeSlug.cfm
-
- <cfset variables.slug = rereplace(lcase(attributes.text), “[^a-z0-9]+”, “-“, “all”)>
- <cfset variables.slug = rereplace(variables.slug, “^-|-$”, “”, “all”)>
-
Usage:
-
-
#slug#
Real-World Use Case: Reusable Pagination Widget
Imagine multiple pages listing records. You want a consistent pagination bar.
Tag file: /customtags/pager.cfm
-
-
-
- <cfset variables.totalPages = ceiling(attributes.totalRows / attributes.pageSize)>
-
Usage:
Benefit: You centralize logic and UI for pagination, ensuring consistency and easier updates.
Use Cases and Patterns
- UI components and wrappers: panels, tabs, alerts, grids, cards
- Content filters/formatters: date formatting, slug generation, currency display
- Reusable data widgets: pagers, breadcrumbs, tag clouds
- Layout composition: wrappers that capture body content and apply a theme or template
- Cross-cutting concerns: standardized error messaging, auditing output, conditional rendering
- Namespaced libraries: via cfimport to avoid name collisions and improve readability
Pros and cons
Pros:
- Encapsulation of presentation and logic
- Clean, declarative usage in templates
- Easy attribute passing and body content handling
- Simple sharing via directories or cfimport as a tag library
Cons:
- Less explicit than calling functions for pure logic
- Overuse can lead to deeply nested templates
- Can write directly to output buffer unless you control it
- For pure Business logic, CFCs/UDFs may be clearer and more testable
Best practices
- Validate inputs with cfparam and provide sensible defaults.
- Use VARIABLES scope for internals; only touch CALLER when you must return values.
- Prefer writing HTML output deliberately; if needed, capture content with cfsavecontent and return a string to the caller.
- Support both tag and cfmodule invocation where appropriate.
- Consider namespacing with cfimport (prefix=”ui”) to avoid conflicts and to group related tags.
- Document attributes (required, optional, types) and side effects (e.g., variables set in caller).
- Keep tags focused; if you find complex Business logic, move it into CFC services and call them from your tag.
- Handle errors predictably with cftry/cfcatch and cfthrow; fail fast on invalid attributes.
- For Deployment, centralize tag directories and configure this.customTagPaths in Application.cfc.
Key Points and Comparisons
-
Custom tag vs. UDF (User-Defined Function):
- Use a custom tag for declarative UI or when wrapping body content is helpful.
- Use a UDF for pure computation and returning values without output concerns.
-
Custom tag vs. CFC method:
- CFCs are better for domain logic, services, and testing.
- Custom tags shine in templating and reusable view components.
-
cf_tag vs. cfmodule:
- cf_tag is concise and relies on tag lookup rules.
- cfmodule is explicit (template path) and works well when you want to bypass lookup or pass attributeCollection.
-
Namespacing with cfimport:
- Use
syntax for clarity and to organize tag libraries.
- Use
Troubleshooting and Common pitfalls
-
“Could not find the ColdFusion custom tag”:
- Check file name and location; verify Application.cfc this.customTagPaths or server Custom Tag Paths.
- Try cfmodule with an absolute/relative template path to confirm availability.
-
Attributes not found or wrong type:
- Add cfparam for each attribute, defining type, default, and required properties.
-
Output appearing in the wrong place:
- Control output carefully; consider cfsavecontent or cfsetting enablecfoutputonly=”true” in complex pages.
-
Body content not captured:
- Ensure you use paired tags and check ThisTag.ExecutionMode on “end”; use ThisTag.GeneratedContent.
-
Scope collisions:
- Always use VARIABLES when creating internal variables; set CALLER variables intentionally and with clear names.
FAQ
What is the difference between a custom tag and cfinclude?
cfinclude simply inserts and executes another CFML file inline, without attribute handling or special scopes. A custom tag adds structured attribute passing, body content wrapping, and tag lifecycle Features (start/end modes), making it better for reusable components.
Indirectly, yes. A custom tag can place values into the CALLER scope or return rendered output. For function-like behavior (returning a value), consider populating CALLER.someVar or capturing output with cfsavecontent and assigning it to a variable.
How do I organize and import a custom tag library?
Place related .cfm tag files in a directory (e.g., /tags/ui). Then use cfimport taglib=”/tags/ui” prefix=”ui” and call them as
Yes, for presentation and templating concerns. Use CFCs for business logic and services; call them from custom tags when rendering UI. This separation keeps views clean and logic testable.
Yes. You can build nested tags (e.g.,
