Radical Design Course by Jack McDade

From the creator of Statamic

Learn how to make your websites standout and be remembered.

Bought Jack McDade's course on design. Going through it now...and it is SO well done!

— Justin Jackson, Transistor.fm

No Cache Tag

When using a broader caching solution (like static caching or the cache tag) you may want to keep a chunk of your template dynamic.

Overview

When using Static Caching or the cache tag, the rendered contents will be remembered for the next request. That's great for performance, but if you have something in there that you'd like to keep dynamic (such as authenticated user data, randomized elements, or listings), you'd normally have to disable static caching for the whole page.

With the nocache tag, you can keep the entirety of the page cached (when using Static Caching) or chunks of the page cached (when using the cache tag), with specific parts remaining dynamic:

{{ nav }} ... {{ /nav }}
 
{{ nocache }}
{{ if logged_in }}
Welcome back, {{ current_user:name }}
{{ else }}
Hello, Guest!
{{ /if }}
{{ /nocache }}
 
{{ content }}
<statamic:nav> ... </statamic:nav>
 
<statamic:nocache>
@if ($logged_in)
Welcome back, {{ $current_user->name }}
@else
Hello, guest!
@endif
</statamic:nocache>
 
{!! $content !!}

Forms

Before the nocache tag existed, a common reason to exclude a page from static caching would be if there was a form on there.

Forms contain a CSRF token for security, which is unique to each user visiting. If you submit the form using someone elses token (which could happen when static caching is enabled) the form won't work.

CSRF tokens are now updated automatically.

However, if your form has logic in it, like validation or success messages, you'll need to keep the form:create dynamic by wrapping in nocache tags.

{{ nocache }}
{{ form:create }}
...
{{ /form:create }}
{{ /nocache }}
<statamic:nocache>
<statamic:form:create>
...
</statamic:form:create>
</statamic:nocache>

Blade

You can use the "no cache" feature in Blade too, although you should use the dedicated Blade directive or the Statamic Elements tag instead of trying to use Statamic::tag.

To keep a part of your template dynamic, move it into a partial then use the @nocache directive just like you would use the @include directive.

@include('mypartial') {{-- this will be cached --}}
@nocache('mypartial') {{-- this will be dynamic --}}

If you'd like to make a section of the current template dynamic without extracting a dedicated partial, you may use the <statamic:nocache> tag:

<statamic:nocache>
...
</statamic:nocache>

Caveats

Most of the time you can probably get away with wrapping something in a nocache tag and it will just work! However there are some situations where you will need to be more aware of how the tag works in order to get predictable results.

When to wrap

For example, let's say you have a collection of stocks. Each entry contains information about the stock, except for the price. You have a custom tag that will get the up-to-the-second stock price using an API.

{{ collection:stocks }}
<li>
{{ company }}
{{ symbol }}
{{ get_price :of="symbol" }}
</li>
{{ /collection:stocks }}
<statamic:collection:stocks>
<li>
{{ $company }}
{{ $symbol }}
<statamic:get_price :of="$symbol" />
</li>
</statamic:collection:stocks>

If you turned on static caching now, then the whole listing would be cached, including the prices.

In this example, you should put a nocache inside the loop.

{{ collection:stocks }}
<li>
{{ company }}
{{ symbol }}
{{ nocache }}
{{ get_price :of="symbol" }}
{{ /nocache }}
</li>
{{ /collection:stocks }}
<statamic:collection:stocks>
<li>
{{ $company }}
{{ $symbol }}
<statamic:nocache>
<statamic:get_price :of="$symbol" />
</statamic:nocache>
</li>
</statamic:collection:stocks>

By putting it inside the loop, it means that the collection:stocks tag wouldn't need to re-query for entries on each subsequent request.

You'd then also rely on static caching invalidation rules to invalidate the entire page when one of your stock entries change.

An example where you would want to put a nocache tag around a loop would be when re-querying every request would be important. For example, a randomized section:

{{ nocache }}
{{ collection:blog featured:is="true" sort="random" }}
...
{{ /collection:blog }}
{{ /nocache }}
<statamic:nocache>
<statamic:collection:blog
featured:is="true"
sort="random"
>
...
</statamic:collection:blog>
</statamic:nocache>

Context

The nocache tag will remember the "context", which are the variables available at that point in the template.

Here we'll use a loop to illustrate what happens:

title: Movie Reviews
reviews:
- name: Top Gun
rating: 58%
- name: Citizen Kane
rating: 99%
- name: Jack and Jill
rating: 3%
{{ reviews }}
<div class="movie">
{{ nocache }}
{{ name }} {{ rating }} {{ title }}
{{ /nocache }}
</div>
{{ /reviews }}
@foreach ($reviews as $review)
<div class="movie">
<statamic:nocache>
{{ $review->name }} {{ $review->rating}} {{ $review->title }}
</statamic:nocache>
</div>
@endforeach
<div class="movie"> Top Gun 58% Movie Reviews </div>
<div class="movie"> Citizen Kane 99% Movie Reviews </div>
<div class="movie"> Jack and Jill 3% Movie Reviews </div>

Within each of those nocache tags, you'd have access to the iteration specific variables:

  • name, rating (e.g. name would be Top Gun, then Citizen Kane, etc)
  • special loop variables (e.g. first, last, count)
  • any cascading variables (e.g. from outer tags, or top level page variables like now, page, etc)

All of the top level variables will be filtered out and replaced with fresh versions on subsequent requests.

Now lets say you update title to Ratings and Top Gun's rating to 60%. The first line out of output will now look like this:

<div class="movie"> Top Gun 58% Ratings </div>

Only the title's change would be reflected, since the reviews value was remembered. If you wanted this to be dynamic, you should wrap the loop rather than putting it inside the loop, as explained in when to wrap.

{{ nocache }}
{{ reviews }}
<div class="movie">
{{ nocache }}
{{ name }} {{ rating }} {{ title }}
{{ /nocache }}
</div>
{{ /reviews }}
{{ /nocache }}
<statamic:nocache>
@foreach ($reviews as $review)
<div class="movie">
<statamic:nocache>
{{ $review->name }} {{ $review->rating }} {{ $review->title }}
</statamic:nocache>
</div>
@endforeach
</statamic:nocache>
<div class="movie"> Top Gun 60% Ratings </div>

Performance

Sometimes, you may notice the contents of the nocache taking a while to load. This is often caused by Statamic hydrating all of the variables from the context.

To improve performance, you can explicitly select the variables you need:

{{ nocache select="this|that" }}
<statamic:nocache
select="this|that"
>
</statamic:nocache>

Alternatively, you can use the @auto placeholder for Statamic to extract the variables from the template (this is similar to the @shallow placeholder in the nav tag):

{{ nocache select="@auto" }}
<statamic:nocache
select="@auto"
>
 
</statamic:nocache>
Hot Tip!

It's worth noting, the nocache tag won't be able to extract variables used inside partials or PHP files like custom tags. You will need to explicitly define the variables you need in these cases.

You can also combine them to extract the variables from the template and add additional ones:

{{ nocache select="@auto|this|that" }}
<statamic:nocache
select="@auto|this|that"
>
 
</statamic:nocache>

Full Measure Static Caching

When using the file static cache driver (aka. "full measure") the pages will be stored as plain html files on your server.

On any pages that use a nocache tag, a small snippet of JavaScript will be injected just before the closing </body> tag.

The nocache fragments will be retrieved from the server using an AJAX request. Because of this, there may be a slight delay before the fragments are replaced. This is similar to a "FOUC" or "flash of unstyled content". In this case, there will be empty <span> tags until they are replaced by the real fragments.

You can optionally define the inner html of these span tags, if you wanted to have a "loading" state, for example.

use Statamic\Facades\StaticCache;
 
StaticCache::nocachePlaceholder('Loading...');
// or
StaticCache::nocachePlaceholder('<svg>...</svg>');

You may wish to run some additional Javascript code once the nocache fragments on the page have been replaced, to enable this a custom event is dispatched that you may register an event listener for.

document.addEventListener('statamic:nocache.replaced', (event) => {
alert('nocache fragments have been replaced!');
});

Database

Behind the scenes, the nocache tag needs to store some data somewhere. This includes the contents of the template chunks, the available variables at that point of the template, which pages it's being used on, etc.

By default, these are stored in the cache. However, with increased traffic or site size, you may eventually run into resource usage issues. In this case, it's possible to store the nocache data in a database.

  1. Configure the database driver in config/statamic/static_caching.php:
    'nocache' => 'database'
  2. Generate the migration:
    php please nocache:migration
  3. Run the migration:
    php artisan migrate
  4. Clear the static cache. Things may be out of sync if you have previously cached pages but the nocache regions don't exist in the DB yet.
    php please static:clear
Docs feedback

Submit improvements, related content, or suggestions through Github.

Betterify this page →