If you’ve used JavaScript frameworks such as React and Vue.js, then you may already be familiar with the concept of state management, which creates a way to communicate and share data across components.
Ideally, users require state management when their application has two or more components that should communicate and share data without having to declare the data in the scope of each component and manually pass them across. Typically, users have some kind of store, which serves as a single source of truth for an application state/data.
Enter Alpine.js, a relatively new JavaScript framework that borrows concepts from React and Vue.js and also has its own implementation of state management through a library called Spruce. Spruce is a lightweight state management library for Alpine.js, and, just like Alpine.js, Spruce is simple and has a small footprint.
In this article, we’ll build a simple to-do application that comprises two components: an input for adding new to-dos and a table displaying the list of to-dos. This will give us the opportunity to access state from a global store inside the two independent components.
To get started, let’s create a new project directory, which we’ll call alpine-spruce-todo
:
mkdir alpine-spruce-todo
Next, create an index.html
file inside the project directory.
cd alpine-spruce-todo touch index.html
Just like Alpine.js, Spruce can be installed either from a CDN or using npm or Yarn. In this tutorial, we’ll use a CDN. Add the following code inside index.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Todo</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css" /> <script src="https://cdn.jsdelivr.net/npm/@ryangjchandler/[email protected]/dist/spruce.umd.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/alpine.min.js"></script> </head> <body> <section class="section"> <div class="container"> <div class="columns"> <div class="column is-three-fifths is-offset-one-fifth">
Because we are using a CDN, we need to pull in Spruce before Alpine.js. For styling, let’s use Bulma CSS, which we’ll also pull in from a CDN.
To start using Spruce, we need to define a global store, which will serve as a single source of truth for all of our application’s components. Let’s create our application’s global store.
Add the following snippet just before the closing body tag:
<script> Spruce.store('todo', { todos: [], }) </script>
We are making use of the CDN build, which means Spruce is available on the window scope. Using the Spruce
variable, we call the store
method to create the store.
The method takes two arguments: the name of the store and the state (data) of the store. Because we are building a todo application, it makes sense to name the store todo
. The store only has one state (todos
), which is an array of todos, which we’ll set to be empty by default.
With a global store in place, we now need to determine how to access the store from our components. Luckily for us, Spruce makes this seamless by exposing a $store
magic property.
Replace the “component goes here” text with the following code:
<div x-data="{}"> <template x-if="$store.todo.todos.length"> <div class="box mt-5"> <table class="table is-fullwidth"> <tbody> <template x-for="(todo, index) in $store.todo.todos" :key="index" > <tr> <td x-text="todo.title"></td> </tr> </template> </tbody> </table> </div> </template> </div>
This is a typical Alpine.js component, but you’ll notice that we didn’t declare any scope for the component. That’s because we want to make use of the data from our global store so that we can access the todo
store using $store.todo
and, subsequently, the store properties.
First, we’ll check to make sure the todos
array contains some todos. Then we’ll display the todos in a table by iterating through the todos
array.
We have seen how to access state in a store. What if we want to modify the state? For that, we follow similar steps to what we’ve already done. To modify a state, we’ll simply reassign a new value to the state. But because we are working with an array as our state, we’ll have to push new items to the array.
Add the following code before the previous component:
<div x-data="todoInput()"> <div class="field"> <div class="control"> <input type="text" class="input" x-model="newTodo" placeholder="What needs to be done?" @keyup.enter="addTodo" /> </div> </div> </div>
This is a typical Alpine.js component, but this time, the component has some scope of its own, which we’ll extract into a separate function called todoInput()
. The input is bound to a newTodo
data, and once the enter key is pressed we call the addTodo
method.
Let’s create the function. Add the snippet below after the code for defining the Spruce store:
function todoInput() { return { newTodo: '', addTodo() { if (!this.newTodo) { return } this.$store.todo.todos.push({ title: this.newTodo, }) this.newTodo = '' } } }
Because we are accessing the store from a function, we need to make use of this
. We simply add the new todo to the todos
array.
In this tutorial, we covered the definition of a state management, and why and when to use it. We’ve also learned how to apply state management in Alpine.js using Spruce, and we saw how to access and modify a store’s state.
To learn more about Spruce, check out the GitHub repo.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBackdrop and background have similar meanings, as they both refer to the area behind something. The main difference is that […]
AI tools like IBM API Connect and Postbot can streamline writing and executing API tests and guard against AI hallucinations or other complications.
Explore DOM manipulation patterns in JavaScript, such as choosing the right querySelector, caching elements, improving event handling, and more.
`window.ai` integrates AI capabilities directly into the browser for more sophisticated client-side functionality without relying heavily on server-side processing.