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>
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")
};
}
<!-- app.js imported above. -->
<div x-data="myData()">
<h1 x-text="greeting"></h1>
</div>
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")
};
}
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;
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>
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();
Top comments (3)
Thanks you
I'm glad this helped!
this.$persist()
should work as well, at least in my example: codepen.io/localhorst/pen/LYXENLBIn this Codepen example the loading order is
Very confusing, but thanks for your post, it helped me.