DEV Community

Cover image for Store UI State in localStorage with Stimulus
Rails Designer
Rails Designer

Posted on • Originally published at railsdesigner.com

Store UI State in localStorage with Stimulus

This article was originally published on Rails Designer


It is pretty common in SaaS apps to store certain user preferences or appearance settings. Things like font-size, theme colors or the open/closed state of an accordion.

Toggling between expanded/collapsed navigation items

(this example from my new SaaS stores the state of the navigation's sections)

You could save those settings to the user, especially if you need to restore those between sessions or different browsers. I got you covered with this article on adding simple preferences for Rails. But if these settings don't need to be persisted, this is a really nice and simple alternative.

It involves a small and reusable JavaScript functions and the browser's localStorage. Let's dive right in.

For this example I am going to store the the theme for a user, either light or dark. When dark, a dark class is added to html-element. This can then be used to target the other element's (like with dark:bg-gray-950 when using Tailwind CSS).

As often with Stimulus, let's write the HTML first. This will guide us what to write next:

<div data-controller="theme">
  <!-- You can show/hide these buttons based on the .dark class -->
  <button data-action="theme#update" data-theme-value-param="dark">
    Lights Off
  </button>

  <button data-action="theme#update" data-theme-value-param="light">
    Lights On
  </button>
</div>
Enter fullscreen mode Exit fullscreen mode

Then the controller:

// app/javascript/controllers/theme_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  update({ params: { value } }) {
    this.#setClass(value);
  }

  // private

  #setClass(theme) {
    document.documentElement.classList.toggle("dark", theme === "dark");
  }
}
Enter fullscreen mode Exit fullscreen mode

While simple on the surface, there are two things to note here: the { params: { value } } part and the two attributes in the toggle method.

First the attributes in the update function. It uses something called destructing. Sounds difficult, but it's not and it is a really cool feature of JavaScript. Let's check it out before going further.

By default the event is passed to the get function that contains the params. You've probably seen this before.

get(event) {
  log(event.params.value)
  // => "light" or "dark"
}
Enter fullscreen mode Exit fullscreen mode

But if you have no need for anything else in the event object, you can omit it, like so:

get({ params }) {
  log(params.value)
  // => "light" or "dark"
}
Enter fullscreen mode Exit fullscreen mode

Or when you want to use destructing, you can do this:

get({ params: { value } }) {
  log(value)
  // => "light" or "dark"
}
Enter fullscreen mode Exit fullscreen mode

Cool, right? Then the toggle("dark", theme === "dark"). The second parameter (theme === "dark") is a boolean force parameter that explicitly sets whether the class should be added (true) or removed (false), rather than just toggling back and forth

💡 Find this all too hard to wrap your head around? Check out JavaScript for Rails Developers. 💡

Okay, great. With the above controller you can toggle between light- and dark mode. That is, if you have your CSS wired up as such, but you notice that once the screen is refreshed, the default screen is back. The chosen theme is not persisted!

For that, let's introduce localStorage! It is a web storage API that lets you store key-value pairs (strings) in the browser.

Let's update the controller to store the chosen value ("dark" or "light").

export default class extends Controller {
  update({ params: { value } }) {
    localStorage.setItem("theme", value);

    this.#setClass(value);
  }
  // …
}
Enter fullscreen mode Exit fullscreen mode

Then upon controller connect, read the value:

export default class extends Controller {
  connect() {
    const theme = localStorage.getItem("theme");

    if (theme) { this.#setClass(theme); }
  }
  // …
}
Enter fullscreen mode Exit fullscreen mode

That's how easy it is store some settings for a user. Note that these values are stored (unencrypted) in their browser. So if they use another browser, the settings are not there. But it's stored also after they restart the browser (unless they clear it).

Besides setItem and getItem, the localStorage API also removeItem and clear().

Top comments (1)

Collapse
 
railsdesigner profile image
Rails Designer

What features you built recently could benefit from the localStorage API?