Use Blogger <b:with> and <b:eval> Tags
Customizing a modern Blogger template often starts as a straightforward exercise in theme design: adjusting typography palettes, setting container widths, or dropping in quick snippets for related widgets. Over time, as dynamic requirements scale—such as responsive multi-resolution images, calculated reading durations, structured schema graphs, multi-author attribution badges, and condition-based layout structures—an XML template can quickly spiral into unmanageable spaghetti markup.
To solve these architectural challenges, Blogger introduced powerful Layouts Version 3 engine tags: <b:with> and <b:eval>. When you master these tools, you can establish local variable scopes, compute inline expressions, chain mathematical or string operations, and build clean, decoupled, and modular XML subroutines without relying on bloaty client-side JavaScript or repetitive template logic.
1. The Architecture of Blogger Layouts Version 3
Before implementing local variables and dynamic evaluation, it is critical to understand how the modern Blogger templating engine processes source markup.
In older theme engines (Layouts Version 1 and 2), theme authors were limited to basic conditional gates (<b:if>, <b:else>) and basic collection loops (<b:loop>). Any complex data transformation—such as dynamically resizing CDN thumbnails, formatting custom timestamp strings, or calculating grid offsets—required either deep nested XML hacks or client-side JavaScript execution after page load.
Layouts Version 3 introduced functional operators, inline lambdas, data filters, and scoped variable tags. This architectural upgrade transforms Blogger's XML parser into a functional server-side template compiler that provides distinct performance benefits:
- Eliminates Cumulative Layout Shift (CLS): Computing dynamic dimensions, classes, and placeholders directly on the server prevents client scripts from mutating and reflowing DOM elements during initial rendering.
- Reduces JavaScript Payloads: Offloading string manipulation, snippet truncation, and date calculations to Blogger's template parser reduces total bundle size and accelerates Largest Contentful Paint (LCP).
- Enforces DRY (Don't Repeat Yourself) Principles: Complex lookups and calculations are declared once in scoped variables rather than repeated across multiple widget files.
2. Deep Dive: The <b:with> Scoped Variable Tag
The <b:with> tag allows you to declare one or more locally scoped variables. These variables exist strictly within the boundaries of the opening <b:with> and closing </b:with> tags.
Core Syntax & Attributes
The syntax requires a variable identifier passed to var and an expression or data path assigned to value:
<b:with var='variableName' value='dataExpression'>
<!-- The variable is available exclusively inside this block -->
<p><data:variableName/></p>
</b:with>
Variable Scoping and Shadowing
Variables declared via <b:with> are strictly scoped. They cannot leak out into parent nodes or adjacent siblings. Furthermore, declaring a variable with the same name in a child block shadows the parent variable without mutating its original value:
<b:with var='state' value='"Active"'>
<p>Parent Context: <data:state/></p> <!-- Outputs: Active -->
<b:with var='state' value='"Archived"'>
<p>Child Context: <data:state/></p> <!-- Outputs: Archived -->
</b:with>
<p>Restored Context: <data:state/></p> <!-- Outputs: Active -->
</b:with>
3. Deep Dive: The <b:eval> Expression Evaluator
While <b:with> assigns and holds state in a named alias, the <b:eval> tag computes an expression and immediately prints the resulting output directly into the HTML response stream.
Core Syntax & Rules
<b:eval expr='dataExpression' />
Standard data tags like <data:post.title/> can only print raw values from the data dictionary. In contrast, <b:eval> lets you execute arithmetic, evaluate ternary conditionals, call string functions, and manipulate arrays inline.
Essential Operators and Built-in Functions
- Arithmetic Operators:
+(addition/concatenation),-(subtraction),*(multiplication),/(division),%(modulo). - Comparison & Logic:
==,!=,>,>=,<,<=,and,or,not, and ternary operator (condition ? valueIfTrue : valueIfFalse). - String & Collection Methods:
length()— returns string length or array size.trim()— removes surrounding whitespace.toLower()/toUpper()— transforms text casing.resizeImage(url, size, ratio)— dynamically rewrites Blogger Google CDN image URLs for specific dimensions and aspect ratios.snippet(text, {length, links, linebreaks})— produces clean, sanitized string excerpts.
4. Architectural Comparison: <b:with> vs. <b:eval>
Understanding when to allocate memory with a variable versus evaluating an expression inline is essential for clean template architecture:
| Feature | <b:with> Tag |
<b:eval> Tag |
|---|---|---|
| Core Purpose | Stores computed values in a local identifier for repeated access. | Evaluates an expression and renders the output immediately. |
| Output Behavior | Silent declaration (produces no HTML output on its own). | Direct output rendering to the markup stream. |
| Structure | Block wrapper: <b:with>...</b:with> |
Self-closing tag: <b:eval expr="..." /> |
| Ideal Use Case | Aliasing deeply nested data paths, storing complex ternary states. | Inline string concatenation, math calculations, single ternaries. |
5. Real-World Practical Design Patterns
Pattern 1: High-Performance Responsive Images with Native CDN Resizing
Instead of outputting low-res default thumbnails across every viewport, you can generate responsive srcset attributes directly in XML using Blogger's native resizeImage() method:
<b:includable id='responsivePostThumbnail' var='post'>
<b:if cond='data:post.featuredImage'>
<b:with var='rawImg' value='data:post.featuredImage'>
<b:with var='img320' value='resizeImage(data:rawImg, 320, "16:9")'>
<b:with var='img640' value='resizeImage(data:rawImg, 640, "16:9")'>
<b:with var='img1024' value='resizeImage(data:rawImg, 1024, "16:9")'>
<picture class='post-thumbnail-wrapper'>
<img class='post-thumbnail'
src='<b:eval expr="data:img640"/>'
srcset='<b:eval expr="data:img320"/> 320w, <b:eval expr="data:img640"/> 640w, <b:eval expr="data:img1024"/> 1024w'
sizes='(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 33vw'
loading='lazy'
alt='<b:eval expr="data:post.title ? data:post.title : data:messages.image"/>'
width='640'
height='360'/>
</picture>
</b:with>
</b:with>
</b:with>
</b:with>
<b:else/>
<!-- Accessible Fallback Placeholder -->
<div class='post-thumbnail-placeholder'>
<span class='icon-photo-fallback' aria-hidden='true'/>
</div>
</b:if>
</b:includable>
Pattern 2: Server-Side Reading Time Calculator
Rather than loading heavy client-side JavaScript libraries to calculate reading times, calculate it directly during server-side compilation:
<b:includable id='postReadingTime' var='postBody'>
<b:with var='charCount' value='data:postBody.length()'>
<b:with var='calcMinutes' value='data:charCount / 1000'>
<b:with var='finalMinutes' value='data:calcMinutes < 1 ? 1 : (data:calcMinutes > 60 ? 60 : data:calcMinutes)'>
<div class='reading-time-badge'>
<svg class='icon-clock' height='16' width='16' viewBox='0 0 24 24'>
<path d='M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm.5-13H11v6l5.2 3.2.8-1.3-4.5-2.7V7z'/>
</svg>
<span>
<b:eval expr='data:finalMinutes'/> min read
</span>
</div>
</b:with>
</b:with>
</b:with>
</b:includable>
Pattern 3: Automated JSON-LD Schema Generation
Valid JSON-LD schema requires sanitized, clean strings. Combining <b:with> and <b:eval> allows you to sanitize excerpts, format fallback authors, and generate clean Schema nodes:
<b:includable id='articleJsonLd' var='post'>
<b:with var='cleanTitle' value='data:post.title.trim()'>
<b:with var='rawSnippet' value='data:post.snippet ? data:post.snippet : data:post.body'>
<b:with var='cleanExcerpt' value='snippet(data:rawSnippet, {length: 160, links: false, linebreaks: false})'>
<b:with var='authorName' value='data:post.author.name ? data:post.author.name : data:blog.title'>
<b:with var='heroImage' value='data:post.featuredImage ? data:post.featuredImage : data:blog.openGraphImage'>
<script type='application/ld+json'>
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "<b:eval expr='data:post.url.canonical'/>"
},
"headline": "<b:eval expr='data:cleanTitle'/>",
"description": "<b:eval expr='data:cleanExcerpt'/>",
"image": "<b:eval expr='data:heroImage'/>",
"author": {
"@type": "Person",
"name": "<b:eval expr='data:authorName'/>"
},
"datePublished": "<b:eval expr='data:post.date.iso8601'/>",
"dateModified": "<b:eval expr='data:post.lastUpdated.iso8601'/>"
}
</script>
</b:with>
</b:with>
</b:with>
</b:with>
</b:with>
</b:includable>
6. Constructing Modular UI Components with <b:includable>
Pairing <b:includable> with <b:with> allows you to create fully isolated, reusable UI components analogous to modern component frameworks:
<!-- Reusable Post Card Subroutine -->
<b:includable id='customPostCard' var='item'>
<b:with var='postUrl' value='data:item.url ? data:item.url : data:item.link'>
<b:with var='commentCount' value='data:item.numberOfComments ? data:item.numberOfComments : 0'>
<b:with var='isFeatured' value='data:item.labels any (l => l.name == "Featured")'>
<article expr:class='"post-card" + (data:isFeatured ? " is-featured-card" : "")'>
<!-- Image Inclusion Sub-component -->
<b:include name='responsivePostThumbnail' data='item'/>
<div class='post-card-content'>
<div class='post-card-meta'>
<span class='meta-date'>
<b:eval expr='data:item.date format "MMMM dd, yyyy"'/>
</span>
<span class='meta-comments'>
<b:eval expr='data:commentCount + (data:commentCount == 1 ? " Comment" : " Comments")'/>
</span>
</div>
<h2 class='post-card-title'>
<a expr:href='data:postUrl'>
<b:eval expr='data:item.title ? data:item.title : data:messages.noTitle'/>
</a>
</h2>
<p class='post-card-snippet'>
<b:eval expr='snippet(data:item.body, {length: 120, links: false})'/>
</p>
<a class='post-card-read-more' expr:href='data:postUrl' expr:aria-label='data:item.title'>
<b:eval expr='data:messages.readMore'/> &rarr;
</a>
</div>
</article>
</b:with>
</b:with>
</b:with>
</b:includable>
Invoking Modular Components in Widget Loops
Once components are encapsulated, primary widget templates become clean and readable:
<b:widget id='Blog1' type='Blog' version='3'>
<b:includable id='main'>
<div class='post-grid-layout'>
<b:loop values='data:posts' var='singlePost'>
<b:include name='customPostCard' data='singlePost'/>
</b:loop>
</div>
</b:includable>
</b:widget>
7. Common Pitfalls & Troubleshooting Guide
When working with Blogger's XML compiler, adhere to strict XML validation rules:
- Unescaped Logical Operators: Never write raw
>or<in condition attributes. Always use escaped entities (>,<,&). - String Literal Quotes: When concatenating strings inside an
exprattribute, enclose literals in distinct quote sets (e.g.expr='data:count + " items"'). - Accidental Self-Closure: Avoid closing
<b:with ... />prematurely as a leaf tag; it must wrap the child elements that consume the variable.
8. Summary & Best Practice Checklist
- Replace heavy JavaScript snippets with server-side
<b:eval>string and math expressions. - Eliminate repetitive data queries by declaring concise local variable scopes with
<b:with>. - Break monolithic widget files into modular, reusable subroutines using
<b:includable>and parameter passing. - Ensure strict XML entity escaping across all dynamic expression attributes to guarantee seamless theme compilation.
