DEV Community

Cover image for A Guide To Svelte Stores
Abdulazeez Abdulazeez for CoderPad

Posted on • Originally published at coderpad.io

A Guide To Svelte Stores

State management is an important aspect of programming that helps to control the flow and behaviour of data within an application. By effectively managing the state of an application, it becomes easier to maintain and scale the application, resulting in improved efficiency and performance.

State management also makes it easier for developers to share data between different components of an application and to organize code in a way that is more efficient and effective. This can help to improve the overall functionality of the application.

In this article, we’ll discuss the svelte store, which is one of the ways we can manage our states in svelte. It is also regarded as the most efficient way to manage complex data.

Prerequisites

To follow through with this article, the following prerequisites are required:

  • Familiarity with the svelte framework

  • Understanding svelte components: Svelte components are reusable, self-contained UI elements that can be easily shared, imported, and composed to build complex user interfaces. 

  • Reactivity: State management in svelte is based on the concept of reactive programming. It means that all the variables used by the application are tracked and can be modified by the user. When a variable is changed, the application will update its state accordingly. This makes it easier to track changes and keep the application running smoothly, as svelte stores are reactive.

Getting started

Let’s first create a basic application using the CoderPad Svelte sandbox to show how we can pass data between components.

In the sandbox, create a new file Child.svelte:

<!--- Child.svelte-->
<script>
  export let greeting
</script>
<span>{greeting}</span>
Enter fullscreen mode Exit fullscreen mode

Replace the content of App.svelte with the code below:

<!--- App.svelte-->
<script>
import Child from "./Child.svelte"
</script>
<h1><Child  greeting ="hello world"/></h1>
<h1><Child  greeting ="hello john"/></h1>
<h1><Child  greeting ="hello micheal"/></h1>
<h1><Child  greeting ="hello doe"/></h1>
<h1><Child /></h1>
Enter fullscreen mode Exit fullscreen mode

Here, we are defining the values of our variable greeting and passing them as props in our parent component, our App.svelte, to the child component, Child.svelte. Our child components then access these props and render them into the browser.

Component props are limited in how much data they can hold. Because of this, complex data sets would need to be broken down into multiple props, which can get messy and complicated.

We might also face a scenario whereby components do not need the props. We still have to send the props through them to be able to pass it further down in the component tree just for a particular component to access the data, which might not be a good thing if we are working with complex data in an application. 

It would be nice to directly make the data available to the required component without manually drilling the props through every level of the component tree. One of the things we can use is the Context API. We then have the option of using a context. 

The context API

The Svelte Context API is a feature that allows components to share states and functions with their children, grandchildren, and descendants. It provides a way of passing values and functions down the component tree without having to pass them at each level explicitly. This makes it easier to maintain and reason about the data flow in an application.

Svelte's context API is ideal for cross-component communication without complicating your codebase with props. The context API is made possible by two Svelte functions: getContext and setContext. Setting an object or value in the context and associating it with a key enables you to make it available anywhere within your app, as shown in the code below:

<script>
  import { setContext } from "svelte";
  const thisObject = {};
  setContext("thisKey", thisObject);
</script>
Enter fullscreen mode Exit fullscreen mode

We want thisKey to be available inside a different component within our app, so we import it using the getContext function.

<script>
  import { getContext } from "svelte";
  const thisObject = getContext("thisKey");
</script>
Enter fullscreen mode Exit fullscreen mode

For example, passing an array of todos between components:

<!-- App.svelte-->
<script>
  import { setContext } from "svelte";
  import Child from "./Child.svelte";
  let todo = ["do chores", "clean", "code", "play football"];
  setContext("key", todo);
</script>
<Child />
Enter fullscreen mode Exit fullscreen mode

In the code above, we set the context key as key and pass todo object as the second argument.

We can now access the todo context from any other components like below:

<!-- TodoCard.svelte-->
<script>
  import { getContext } from "svelte";
  let todo = getContext("key");
</script>
<p>{todo}</p>
Enter fullscreen mode Exit fullscreen mode

Although the Svelte context API is great for small applications, it becomes increasingly more work to manage the state simply and concisely as applications scale up.  

Additionally, the context API does not provide any built-in methods for tracking changes to the data, making it difficult to track which pieces of data are outdated and need to be updated. For these reasons, it is better to use a store for managing and passing complex data between components.

Introducing svelte stores

A Svelte store is a JavaScript object that holds data similar to a variable. However, unlike a variable, Svelte stores can be observed, meaning that any part of the application can monitor changes to the store and respond accordingly. 

It also makes it easier to keep track of changes to the data and makes it easier to modify the data when needed. The store is also the main way of sharing data between different parts of an application, making it easier to keep the code organized and maintainable.

The svelte store offers a solution that helps developers overcome these challenges by helping us manage our state. It detaches and manages it in an entirely different place so components can’t become bloated with unnecessary data. This makes Svelte Stores ideal for managing state in Svelte applications, as we can use them to store application data and allow components to reflect changes in the store.

Svelte has two primary types of stores, writable and readable stores. Svelte also offers a special type of store called a derived store. 

Writable Stores

Writable stores are basically objects that hold values or attributes that various app components may access. They are the sources of information storage. Let's use the writable store to store a value we can later change or pass around in our application. We'll create our store as a JavaScript file, add some values and then export it:

 

<!-- weather.js -->
<script>
  import writable from 'svelte/store'
  export const weather = writable('sunny')
</script>
Enter fullscreen mode Exit fullscreen mode

We can then import inside any other component where we'll need the data:

<script>
  import { weather } from './weather.js'
</script>
Enter fullscreen mode Exit fullscreen mode

We can alter the value in a writable store, and when we need to change the value of weather in any component where we imported it, we can use the unset() method. The unset() method is used to remove a value that has been previously set in a component's state:

<!-- Newweather.svelte -->
<script>
  import { Weather } from './Weather.js'
  city.set('Rainy')
</script>
Enter fullscreen mode Exit fullscreen mode

We can also use the update() method. This will help us invoke a callback in which the current value is passed as an argument:

<!-- Newweather.svelte -->
<script>
  import { Weather } from './Weather.js'
  const newWeather = 'Rainy'
  function changeWeather() {
    Weather.update(existing => newWeather)
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Alternatively, we could have components use Svelte's subscribe() function to monitor changes to the value we put in our store. The subscribe method in Svelte is used to add a reactive effect to a component:

<script>
  import { Weather } from './Weather.js'
  const watch = Weather.subscribe(value => {
    console.log(value);
  });
</script>
Enter fullscreen mode Exit fullscreen mode

Readable stores

Readable stores, like writable stores, hold objects but cannot be modified by external components. You must set the value of a readable store when you create it. We use Readable stores to handle data that must be immutable. Below is an example of fetching the current date:

<!-- date.js -->
import { readable } from 'svelte/store'

export const date = readable(new Date())

<!-- app.svelte -->
<script>
import {date} from "./date.js"
</script>

<h1>Today's date is{$date}</h1>
Enter fullscreen mode Exit fullscreen mode

Derived Stores

Derived stores, as the name implies, are derived from another store. The value of a derived store is updated when the store from which it was derived is updated. Derived stores are simply readable stores, but their values come from somewhere else, at least partially:

import { derived } from "svelte/store";
import { date } from "./store.js";
export const test = derived(date, ($date) => {
  return (
    $counter.getHours() +
    ":" +
    $counter.getMinutes() +
    ":" +
    $counter.getSeconds()
  );
});
Enter fullscreen mode Exit fullscreen mode

Our derived store can then be imported into another component as follows:

<!-- App.svelte -->
<script>
  import { test } from "./derived.js";
</script>
<h1>
  {$test}
</h1>
Enter fullscreen mode Exit fullscreen mode

Svelte store example: Building a student data list

To showcase how Svelte store could be used in real-time, we will be building an app where we’ll be able to manage state in svelte. 

We can set up a new project folder and install svelte for this project example by following the steps mentioned earlier, or we can use the one created previously.

Project scope

For this project, we’ll be building a student data list that will display some student data, and we’ll do that with the help of a store. 

Creating a store

We’ll be creating a store that will contain some student data. Inside the src folder, create a folder called stores and create a file called students.js. This file will define the methods for updating our store's values and resetting them to their default values when needed. Our students.js will contain the following:

import { writable } from "svelte/store";
const STUDENTS = [
  { name: "john", surname: "doe", age: 17 },
  { name: "micheal", surname: "angelo", age: 21 },
];

const { subscribe, set, update } = writable(STUDENTS);
const addStudent = (student) =>
  update((students) => {
    return [...students, student];
  });

const reset = () => {
  set(STUDENTS);
};

export default {
  subscribe,
  addStudent,
  reset,
};
Enter fullscreen mode Exit fullscreen mode

Now we’ll import the exported methods, which are the subscribe, addStudent, and reset, into the component we’ll create shortly and use them to subscribe, add new values and reset our student list.

Creating our components

Next, we will create components that accept the methods exported from our store. We'll be creating two components: StudentPage.svelte, which will be used to add new student data, and StudentList, which will display the list of students that have been added. 

In StudentPage.svelte, add the following:

<script>
  import students from "./stores/students";
</script>
<div class="studentPage">
  {#each $students as student}
    <p>{student.name} {student.surname}</p>
    <p>{student.age}</p>
    <hr />
  {/each}
</div>
Enter fullscreen mode Exit fullscreen mode

We’ll make our StudentList a form so we can add new student data as well as reset them:

<script>
  import students from "./stores/students";
  // binding values
  let name = "";
  let surname = "";
  let age = "";
  // submit student
  const submitStudent = () => {
    students.addStudent({ name, surname, age });
    // reset values
    name = surname = age = "";
  };
</script>

<div class="studentList">
  <input type="text" bind:value={name} placeholder="Enter student make" />
  <input type="text" bind:value={surname} placeholder="Enter student surname" />
  <input type="year" bind:value={age} placeholder="Enter student age" />
  <input type="submit" on:click={submitStudent} value="Include Student" />
  <input type="submit" on:click={students.reset} value="Reset Student list" />
</div>
Enter fullscreen mode Exit fullscreen mode

Let’s add some basic styling to our components. In our app.css, paste this in:

.studentList {
  margin: 0 auto;
  box-shadow: 0 0 30px 0;
  height: auto;
  max-width: 410px;
  background-color: rgb(231, 231, 231);
  padding: 20px;
}

input {
  margin: 30px 0 0;
  height: 30px;
  width: 100%;
  padding: 0 10px;
  outline: none;
  border: none;
  background-color: rgb(231, 231, 231);
  border-bottom: 1px solid #000;
  font-size: 1.1rem;
  transition: .3s;
}

button {
  border-radius: 6px;
  margin: 20px 0 0;
  height: 40px;
  width: 100%;
  color: #fff;
  padding: 0;
  outline: none;
  cursor: pointer;
  transition: .3s;
  border: none;
}

.button {
  background-color: rgba(26, 26, 204, 0.582);
}

.button:hover {
  background-color: rgb(26, 26, 204);
}

.reset {
  background-color: rgb(189, 42, 42);
}

.reset:hover {
  background-color: rgb(255, 0, 0);
}

/* studentpage styling */
.studentPage {
  padding-top: 30px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.table-bordered {
  border-width: 1px 1px 1px 0px;
  border-style: solid solid solid none;
  border-color: #fff #fff #fff -moz-use-text-color;
  border-collapse: collapse;
  border-radius: 4px;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  max-width: 100%;
  overflow-x: auto;
}

.table-bordered th,
.table-bordered td {
  min-width: 150px;
  padding: 8px;
  line-height: 20px;
  text-align: left;
  vertical-align: top;
  border: 1px solid #fff;
}

.table-bordered th {
  background-color: rgb(88, 83, 83);
  color: #fff;
}
Enter fullscreen mode Exit fullscreen mode

We’ll then import into each of our components using:

import "./app.css";
Enter fullscreen mode Exit fullscreen mode

The last thing we’ll do is import both our components into our app.svelte:

<script>
  import StudentList from "./StudentList.svelte";
  import StudentPage from "./StudentPage.svelte";
</script>
<div>
  <StudentList />
  <StudentPage />
</div>
Enter fullscreen mode Exit fullscreen mode

We just created an app and managed it using svelte stores! Play with the application in this sandbox.

Conclusion

Svelte stores are an effective way to manage and access data in your Svelte applications. They provide a centralized way to store data, making it easier to keep track of changes and make modifications when needed. Using a Svelte store is straightforward and can make your code cleaner and more maintainable.

Top comments (0)