Skip to main content
Ben Nadel at cf.Objective() 2017 (Washington, D.C.) with: Dan Sirucek
Ben Nadel at cf.Objective() 2017 (Washington, D.C.) with: Dan Sirucek

Russian Doll Content Wrapping With CFSaveContent In ColdFusion

By
Published in Comments (11)

In web development, the term "Russian Doll" is sometimes used to refer to content that is wrapped inside another piece of content of the same type. This is based on the Russian Doll toy (Matryoshka), which has a multitude of smaller toys contained within it. In the past, I've looked at using the Russian Doll pattern for error handling in Node.js as well as for error handling in ColdFusion. But, its value extends beyond just errors - I often use the CFSaveContent tag to build up a content payload from the outside in. And, I thought it would make for a nice example.

If you dig into web application frameworks like Framework One (FW/1), they often use Russian Doll content nesting to render page responses. They do this by storing content into a given variable; and then, redefine said variable, over and over again, referring to itself in the subsequent expressions.

This might sound strange in the abstract, but we actually use this kind of approach all the time. Consider incrementing a numeric value:

value = ( value + 1 );

In this assignment, the value variable is being redefined using itself as part of the evaluated expression. When it comes to wrapping content, it's the same exact thing. And, with ColdFusion, the CFSaveContent tag makes this Russian Doll nesting extremely easy!

In the following ColdFusion template, I'm going to use several successive CFSaveContent tags. Each tag is going to assign content to the body variable; and, all but the first tag are going to include the body variable within their own tag bodies:

<cfoutput>

	<!--- Define the initial BODY content. --->
	<cfsavecontent variable="body">
		<p>
			This is the body!
		</p>
	</cfsavecontent>


	<!--- Wrap the BODY content in more content. --->
	<cfsavecontent variable="body">
		<header>
			This is the header.
		</header>
		<main>
			#body#
		</main>
		<footer>
			This is the footer.
		</footer>
	</cfsavecontent>


	<!--- Wrap the BODY content in EVEN MAOR content! --->
	<cfsavecontent variable="body">
		<!doctype html>
		<html lang="en">
		<head>
			<title>
				ColdFusion is Amaze-balls!
			</title>
		</head>
		<body>
			#body#
		</body>
		</html>
	</cfsavecontent>


	<!--- Output the doubly-wrapped content. --->
	#body#

</cfoutput>

As you can see, each subsequent CFSaveContent tag wraps the previous tag's body value by interpolating the #body# string into its own tag body evaluation. Each body value is "Russian Doll nested" back into itself.

If we then run this ColdFusion page and look at the generated source, we get the following output - I've decorated each branch of the DOM (Document Object Model) tree that was supplied by a CFSaveContent tag:

Screenshot of rendered HTML.

The CFSaveContent tag is definitely one of the unsung heroes of the CFML language. I use it all the time to store generated content into a variable (this is especially helpful in ColdFusion custom tags). And, when composing layouts, the CFSaveConent tag combined with Russian Doll nesting is a force to recon with!

Want to use code from this post? Check out the license.

Reader Comments

238 Comments

@Ben,

I love this (hidden from me) gem! I really hadn't ever considered using it in this way, but brilliant 👌 wunderbar 🎉 excellent 🙌

simple, elegant AND OH SO useful!

15,848 Comments

@Chris,

Awesome! That's exactly what I like to over-share -- you never know what someone has or has not seen yet. Never hurts!

238 Comments

@Ben,

Indeed. Much appreciated. Everything we've ever learned we've once learned for the first time. Today is that day for this particular topic 😂

4 Comments

Cool example! I've only recently started using cfsavecontent to address a problem with buffer over-run, so I'm wondering if there are any downsides I'm not seeing. I've got an application that has one page that can (in very limited circumstances) be very large and trigger the "Unable to add text to HTML HEAD tag." error. As I'm on a shared server, I can't change the maximum buffer size, so I put the whole page into cfsavecontent and problem fixed. The page contains a form and actions, but as far as I can tell, still functions normally. Do you know if there anything I should be looking out for?

15,848 Comments

@Stuart,

I'm not familiar with the buffer over-run problem that you're speaking of. I know that you can configure (in the CF Admin) the maximum size of the request; which is maybe what you're talking about (but can't change on shared hosting). That said, I have on idea why putting things into a cfsavecontent would have any impact on that since you're still sending the same amount of data over the request in the end.

Very curious! That said, I can't think of any significant downside. The only possible downside that I can think of is that using cfsavecontent limits your ability to "stream" data back to the browser. What I mean by that is that if you entirely build-up the page on the server first, you can't start sending those bytes down the browser until you've already fetched all the data. However, in theory, if you built your HTML head first, then flushed it, then built up the page content, you could be more optimal. Of course, that approach also has lots of problems, like what happens if you flushed the head and then need to do a redirect -- you can't.

All to say, I wouldn't have any concern about using cfsavecontent to construct your page. That's what I'm doing and I don't have any problems.

4 Comments

@Ben

Thanks so much for the quick reply, and sorry it took so long for mine (not sure if I should have, but I didn't get an email saying you replied)! Yeah, this is the first time I've come across this buffer issue, and I had the same thought as to why this should work when the amount of data should be the same. Either way, it seems to address things without side effects, and I feel better reading your reply. Thanks again!

15,848 Comments

Woot! 🎉 🎊 💥 💯 Thank you for confirming. I can't believe this has been broken since like last February (2023).

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel