<cfmodule> calls a custom tag the same way the cf_ prefix does — same Attributes/Caller scope behavior, same isolation — but without needing the tag's file to live in a specific directory or follow the cf_ naming convention. It identifies the tag by an explicit path or a dotted name instead.
Learning Objectives
After completing this lesson, you'll be able to:
- Call a custom tag with cfmodule using either the template or name attribute.
- Pass many attributes at once with attributeCollection.
- Avoid a real Adobe ColdFusion gotcha where template and attributeCollection can't be combined.
template vs name
These two attributes are mutually exclusive — use exactly one of them per call.
<cfmodule template="/layout/header.cfm" pageTitle="Home">
<cfmodule name="layout.header" pageTitle="Home">
name resolves to a subdirectory under the configured CFML tag root, using dots in place of slashes — layout.header means the layout/header.cfm tag file.
Passing Many Attributes at Once: attributeCollection
<cfscript>
attrs = {theme: "dark", pageTitle: "Welcome!"};
</cfscript>
<cfmodule template="/layout/header.cfm" attributeCollection="#attrs#">attributeCollection can only be used once per tag call, and its keys become the same Attributes scope entries as if each had been passed individually.
A Real Gotcha: template Can't Combine with attributeCollection (Adobe ColdFusion)
On Adobe ColdFusion specifically, trying to pass template itself inside an attributeCollection struct (instead of directly as its own attribute) fails with a genuinely unhelpful, unrelated-looking error rather than a clear message about the real problem. Lucee doesn't have this restriction.
<cfset attrs = {template: "/layout/header.cfm", pageTitle: "Home"}>
<cfmodule attributeCollection="#attrs#"><cfset attrs = {pageTitle: "Home"}>
<cfmodule template="/layout/header.cfm" attributeCollection="#attrs#">Always pass template as its own explicit attribute on Adobe ColdFusion, even when using attributeCollection for everything else.
cfmodule vs cfinclude vs Custom Tag (cf_)
| cfinclude | cf_ Custom Tag | cfmodule | |
|---|---|---|---|
| Scope | Shared with caller | Isolated | Isolated |
| Identifies the tag by | File path | Filename + cf_ prefix | template path or dotted name |
| Storage location rules | None (just a path) | Specific directories required | None — any path works |
| Bulk attribute passing | N/A | No | Yes, via attributeCollection |
cfmodule and cfimport solve a similar problem (calling a custom tag without the cf_/CustomTags-directory rules) in different ways — cfimport sets up a whole namespace once and calls tags repeatedly under a short prefix, while cfmodule identifies one specific tag per call. For a tag used constantly across a codebase, cfimport's prefix tends to read cleaner; for an occasional or dynamically-chosen tag, cfmodule's explicit path is simpler. See the cfimport lesson for the namespace approach in full.
Common Beginner Mistakes
Specifying both template and name on the same call
They're mutually exclusive — pick one way to identify the tag, not both.
Putting template inside attributeCollection on Adobe ColdFusion
This fails with a cryptic, unrelated-sounding error on Adobe ColdFusion specifically (works fine on Lucee). Always pass template as its own direct attribute.
Best Practices
- Use cfmodule when the tag to call is determined dynamically, or lives somewhere that doesn't fit the CustomTags directory convention.
- Keep template as an explicit attribute even when passing everything else through attributeCollection, for Adobe ColdFusion compatibility.
Interview Questions
What's the difference between cfmodule's template and name attributes?
template identifies the tag file by path (relative or mapped). name identifies it by a dotted path resolved under the configured CFML tag root — they're mutually exclusive.
What does attributeCollection let you do?
Pass a whole struct of attributes to the tag at once, instead of listing each one individually — its keys become entries in the tag's Attributes scope.
What's the practical benefit of cfmodule over the cf_ prefix convention?
The tag file doesn't need to live in a specific directory or follow the cf_ naming convention — cfmodule identifies it directly by path or dotted name instead.
Is template mandatory on <cfmodule>?
Not by itself — template and name are mutually exclusive, so the real requirement is that exactly one of the two has to be provided, not specifically template every time.
How does cfmodule's scope behavior differ from cfinclude's?
cfinclude shares the calling page's Variables scope completely — no isolation, no parameters. cfmodule runs the tag in its own isolated scope, the same as a cf_-prefixed custom tag, receiving input only through Attributes (or attributeCollection) and returning data only through Caller.
Give a concrete example of when you'd choose cfmodule over cfinclude.
A reusable page-header component that needs different data on different pages (a page title, an active nav item, a theme) is a good fit for cfmodule — <cfmodule template="/layout/header.cfm" pageTitle="Home" activeNav="home">. cfinclude couldn't take those as real parameters at all; the header file would have to rely on whatever variables happened to already exist in the calling page's scope.
Summary
In this lesson, you called custom tags with cfmodule using template and name, passed multiple attributes at once with attributeCollection, and covered a real Adobe ColdFusion-specific gotcha combining template with attributeCollection.
What's Next?
The next lesson covers Layouts — using these code-reuse tools together to build a consistent header/footer structure across a whole application.