Introduction
Fragment caching stores rendered HTML snippets so Rails doesn't have to re-render them on every request. It's one of the most effective ways to speed up view rendering in Rails applications.
Key Concepts
- Fragment Cache: A stored HTML snippet keyed by a record's
cache_key_with_version, which includes model name, ID, andupdated_attimestamp. - Cache Digest: Rails automatically includes a digest of the template in the cache key, so changing the template invalidates the cache.
- Collection Caching: Efficiently caching every item in a collection with a single multi-read from the cache store.
Real World Context
A product listing page rendering 50 cards with prices, images, and reviews can go from 200ms to 20ms with fragment caching. Each card is cached individually, so updating one product only invalidates that card's cache.
Deep Dive
Basic Fragment Cache
erb<% @posts.each do |post| %> <% cache post do %> <article> <h2><%= post.title %></h2> <p><%= post.body %></p> <span>By <%= post.author.name %></span> </article> <% end %> <% end %>
The cache key is based on post.cache_key_with_version — cache automatically invalidates when the post is updated.
Collection Caching
erb<%= render partial: 'post', collection: @posts, cached: true %>
Rails fetches all cache entries in one read_multi call!
Touch to Invalidate Parent
rubyclass Comment < ApplicationRecord belongs_to :post, touch: true end
When a comment is created or updated, post.updated_at changes, invalidating the post's cache.
Cache Store Configuration (Rails 8.1)
ruby# config/environments/production.rb # Solid Cache (Rails 8 default — database-backed, no Redis needed) config.cache_store = :solid_cache_store # Redis (alternative for high-throughput caching) config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'], expires_in: 1.day }
Common Pitfalls
- Forgetting touch on associations — If a comment changes but the post's
updated_atdoesn't update, the cached post HTML will show stale comment counts. - Caching user-specific content — Include the current user in the cache key if the fragment varies per user:
cache [post, current_user].
Best Practices
- Use collection caching —
render partial: ..., cached: trueis far faster than individual cache calls because it uses a single multi-read operation. - Use Solid Cache in production — Rails 8's default cache store is database-backed, requires no Redis, and handles most workloads efficiently.
Summary
- Fragment caching stores rendered HTML keyed by record version.
- Collection caching reads all entries in one multi-read call.
- Use
touch: trueon associations to auto-invalidate parent caches. - Rails 8 defaults to Solid Cache (database-backed, no Redis needed).
Code Examples
ruby
# In your view — cache each post card
<% cache post do %>
<article>
<h2><%= post.title %></h2>
<p><%= post.body %></p>
</article>
<% end %>
# Even faster — collection caching with multi-read
<%= render partial: 'post', collection: @posts, cached: true %>