DEV Community

Tyler Smith
Tyler Smith

Posted on

Using Alpine's Persist plugin in a separate JavaScript file

Alpine's Persist plugin stores the value of an Alpine variable in local storage, which allows it to persist across page navigations and still be present on subsequent visits to the site. It is accessed through the magic $persist function.

<div x-data="{greeting: $persist('hello world')}">
  <input type="text" x-model:value="greeting" />
</div>
Enter fullscreen mode Exit fullscreen mode

The problem

When a page or component has lots of functionality, Alpine's x-data attribute can get a little unwieldy.

Thankfully you can extract the x-init data into a function. Unfortunately, the following won't work:

// app.js

function myData() {
  return {
    greeting: $persist("hello world")
  };
}
Enter fullscreen mode Exit fullscreen mode
<!-- app.js imported above. -->

<div x-data="myData()">
  <h1 x-text="greeting"></h1> 
</div>
Enter fullscreen mode Exit fullscreen mode

The app.js script file doesn't have access to the magic $persist function. Using this.$persist won't work either.

The solution

Fortunately, it's simple to use Alpine's Persist plugin when defining x-data as a function in a script file. All you need to do is replace $persist with Alpine.$persist:

// app.js

function myData() {
  return {
    greeting: Alpine.$persist("hello world")
  };
}
Enter fullscreen mode Exit fullscreen mode

With this change, you will be able to use the persist function in a separate script file. This works because under the hood, the persist plugin just binds itself to the Alpine object (source code).

Gotchas

There are two gotchas you can run into when trying to get this all to work.

Gotcha 1: The x-data function must be globally accessible.

If you use Webpack, Vite or nearly any other bundler, the functions you define in your JavaScript files won't be globally accessible. You can test this by trying to call the function directly in the JavaScript console within the browser's developer tools. If you run myData() in the console and get an error that says Uncaught ReferenceError: myData is not defined, it means that Alpine can't see the myData() function either.

To fix this, assign the myData function to the window object:

// app.js

function myData() {
  return {
    greeting: Alpine.$persist("hello world")
  };
}

window.myData = myData;
Enter fullscreen mode Exit fullscreen mode

window is the global scope in JavaScript, which means myData() will now be accessible anywhere.

Gotcha 2: The x-data function must be defined before Alpine initializes

In Alpine.js, the order in which scripts load matters. You must make sure that the script where your x-data function is defined loads before Alpine.

If you're loading Alpine through CDN script tags, you can ensure that the x-data function is defined before Alpine initializes by including the script where it is defined before the Alpine scripts:

<!DOCTYPE html>
<html>
  <head>
    <!-- Our script comes first -->
    <script defer src="app.js"></script>
    <script defer src="https://unpkg.com/@alpinejs/persist@3.x.x/dist/cdn.min.js"></script>
    <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
  </head>
  <body>
    <div x-data="myData()">
      <input type="text" x-model:value="greeting" />
    </div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

If you're using Alpine as an NPM package, you need to make sure that you define your x-data function before you call Alpine.start():

import Alpine from "alpinejs";
import persist from "@alpinejs/persist";

/** Our function comes before Alpine.start() */
function myData() {
  return {
    greeting: Alpine.$persist("hello world")
  };
}

window.myData = myData;

window.Alpine = Alpine;
Alpine.plugin(persist);
Alpine.start();
Enter fullscreen mode Exit fullscreen mode

Oldest comments (3)

Collapse
 
logispren profile image
Angel Bran

Thanks you

Collapse
 
tylerlwsmith profile image
Tyler Smith

I'm glad this helped!

Collapse
 
marcusatlocalhost profile image
Marcus • Edited

this.$persist() should work as well, at least in my example: codepen.io/localhorst/pen/LYXENLB
In this Codepen example the loading order is

html
  body
    script(defer src="persist.js")
    script(defer src="alpine.js")
    div
     ul
       li etc...

    script(src="list.js")
    script(src="pen.js") // my script in codepen
Enter fullscreen mode Exit fullscreen mode

Very confusing, but thanks for your post, it helped me.