# Nifty way to (fully) client-side cache a turbo-frame

**URL:** <https://discuss.hotwired.dev/t/nifty-way-to-fully-client-side-cache-a-turbo-frame/2103>\
**Category:** General\
**Created:** [January 24, 2021, 1:56pm UTC](https://discuss.hotwired.dev/t/nifty-way-to-fully-client-side-cache-a-turbo-frame/2103 "2021-01-24T13:56:15Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![dreetje](https://yyz1.discourse-cdn.com/flex027/user_avatar/discuss.hotwired.dev/dreetje/32/1518_2.png) [@dreetje](https://discuss.hotwired.dev/u/dreetje)\
**Post date:** [January 24, 2021, 1:56pm UTC](https://discuss.hotwired.dev/t/nifty-way-to-fully-client-side-cache-a-turbo-frame/2103/1 "2021-01-24T13:56:15Z")

</div>

I ran into an issue with an API I am using that in its terms forbid any kind of server side caching, but allowed client side caching.

I ended up doing a simple trick in my Rails app:

```auto
<!-- index.html.erb -->
<turbo-frame id="api-results" src="/api_results"/>

<!-- api_results.html.erb -->
<turbo-frame id="api-results">
  <div data-controller="cache" data-cache-duration-value="60">
  <!-- ... all the results -->
</turbo-frame>

```

And two reusable Stimulus controllers:

```javascript
// cache_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static values = { duration: Number }

  connect() {
    var parent = this.element.parentNode

    if (parent.src) {
      parent.dataset.controller = "cached"
      parent.dataset.cachedFetchAfterValue = Date.now() + (this.durationValue * 1000)
      parent.dataset.cachedSrcValue = parent.src
      parent.src = ''
    }
  }
}

// cached_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static values = { fetchAfter: Number, src: String }

  connect() {
    if (Date.now() >= this.fetchAfterValue) {
      this.element.src = this.srcValue;
    }
  }
}

```

Whenever content is loaded that I want to cache client side, I add the prefix div, which, when connected, wipes the `src` of the parent turbo-frame, and adds a controller, that, when connected, checks if it needs to re-add it after x seconds.

---

<div class="post-metadata">

**Author:** ![BenNadel](https://yyz1.discourse-cdn.com/flex027/user_avatar/discuss.hotwired.dev/bennadel/32/3067_2.png) [@BenNadel](https://discuss.hotwired.dev/u/BenNadel)\
**Post date:** [February 12, 2023, 5:29pm UTC](https://discuss.hotwired.dev/t/nifty-way-to-fully-client-side-cache-a-turbo-frame/2103/2 "2023-02-12T17:29:29Z")

</div>

This is really interesting – can you expand on this a bit? I’m curious to understand how long the cache “lives” for in conjunction with navigating around the app? If a user were to leave this page, and then come back, Turbo Drive should fetch the _fresh_ version of the page which would lead to a non-cached version?

This is very timely for me since I’ve was literally just playing with Turbo Frames this morning, and have found that [adding `data-turbo-permanent` to a Turbo Frame](https://www.bennadel.com/blog/4406-defer-loading-using-permanent-turbo-frames-in-hotwire-and-lucee-cfml.htm) will maintain the state across pages, which can have a nice caching affect. I think perhaps the two approaches can play very nicely together, but I want to understand yours better.
