Beyond forms, Tag Helpers provide powerful tools for managing client-side resources and server-side rendering decisions. The environment Tag Helper conditionally renders content per environment. The script and link Tag Helpers add CDN-with-fallback, cache-busting, and file globbing. The cache Tag Helper stores rendered HTML fragments server-side, eliminating repeated database calls for content that changes infrequently. These features are used in every production ASP.NET Core layout file.
Environment Tag Helper
@* โโ Render content only in specific environments โโโโโโโโโโโโโโโโโโโโโโโโโโ *@
<environment include="Development">
@* Only shown in Development โ full, unminified assets for debugging *@
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<script src="~/lib/jquery/dist/jquery.js"></script>
</environment>
<environment exclude="Development">
@* Staging and Production โ minified CDN assets with local fallback *@
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only"
asp-fallback-test-property="position"
asp-fallback-test-value="absolute"
crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous">
</script>
</environment>
asp-fallback-src / asp-fallback-href mechanism works by injecting a small inline script after the CDN <script> tag that tests whether the CDN resource loaded successfully (asp-fallback-test="window.jQuery" checks if jQuery is defined). If the test fails (CDN unavailable), the script dynamically loads the fallback local resource. This gracefully handles CDN outages at the cost of a slightly larger HTML page due to the inline test script.asp-append-version="true" on local static file references in layouts. It appends a hash of the file’s content as a query string: /css/site.css?v=r3Jnzc7FHmMObFj4Z0. When you update the CSS file, the hash changes and browsers download the new version instead of serving the cached old version. Without this, a deployment that changes CSS or JS files may leave users with stale cached versions for hours or days, depending on their browser cache settings.<cache expires-after="@TimeSpan.FromMinutes(10)">) stores rendered HTML in the server’s in-memory cache. On a multi-server deployment (multiple app instances), each server has its own independent in-memory cache โ users may see different cached content depending on which server handles their request. For consistent caching across multiple instances, use the distributed cache Tag Helper (<distributed-cache name="...">) backed by Redis or SQL Server, which stores the HTML in a shared external cache.Cache Tag Helper
@* โโ Cache a rendered HTML fragment server-side โโโโโโโโโโโโโโโโโโโโโโโโโโโโ *@
@* Cache the popular posts sidebar for 10 minutes โ avoids database query per request *@
<cache expires-after="@TimeSpan.FromMinutes(10)">
<aside class="sidebar">
<h4>Popular Posts</h4>
@await Component.InvokeAsync("PopularPosts")
</aside>
</cache>
@* Cache with a vary-by key โ different cache entries per user *@
<cache varies-by-user="true" expires-after="@TimeSpan.FromMinutes(5)">
<div class="user-notifications">
@await Component.InvokeAsync("NotificationBell")
</div>
</cache>
@* Cache with a named key โ invalidate programmatically via IMemoryCache *@
<cache name="homepage-hero" expires-sliding="@TimeSpan.FromHours(1)">
<section class="hero-banner">
@await Component.InvokeAsync("HeroBanner")
</section>
</cache>
@* Distributed cache Tag Helper โ for multi-server consistent caching *@
@* Requires: builder.Services.AddStackExchangeRedisCache(...) *@
<distributed-cache name="global-stats"
expires-after="@TimeSpan.FromHours(1)">
@await Component.InvokeAsync("SiteStatistics")
</distributed-cache>
Common Mistakes
Mistake 1 โ Using the cache Tag Helper without a backend on multi-server deployments
โ Wrong โ in-memory cache is per-server; different users on different servers see inconsistent cached content.
โ Correct โ use distributed-cache Tag Helper with Redis or SQL Server for multi-server deployments.
Mistake 2 โ Not using asp-append-version on static files (stale cache after deployment)
โ Wrong โ CSS/JS updates not picked up by users with cached old versions.
โ
Correct โ always add asp-append-version="true" to all <link> and <script> references for local files.