Guide··TauriState

What you need to know about persistent state in Tauri apps

There are many different ways to store data in a persistent way, in this post we'll a few options that are available to Tauri apps.

Guilherme Oenning
Guilherme Oenning @goenning

Let’s start by defining what persistent state really is. Persistent state is basically any piece of data stored on a user’s computer which is not lost when the app is closed. This is in contrast to temporary state, which often held in memory and is gone after the app is closed.

Persistent state can also be stored in the cloud, which truly shines for cross-platform synchronization, such as from Web to Desktop, or Desktop to Mobile. However, this is out of scope for this post. We’ll focus on local state only.

It might be useful to split persistent state into two categories as the treatment is different for each:

  • User Data is often generated by the user, or fetched from the internet and stored locally. It may be a list of items on a To Do app, a list of music on a music player, or a list of contacts on a CRM.
  • App Preferences are the settings used to customize the app, such as the theme, language, default sorting column or window size. Some preferences may be set by the user, but others may be automatically set by the app itself, such as the last window size.

Let’s explore a few options for storing both User Data and App Preferences in Tauri apps.

1. Webview Local Storage

This is the most primitive option for storing persistent state. It’s built into the webview, and available to JavaScript via the window.localStorage object. It’s a simple key-value store, and the values are stored as strings.

localStorage.setItem("app-theme", "dark");

const theme = localStorage.getItem("app-theme");

One of the biggest benefits of this option is the simplicity. Being synchronous also makes it easy to use in a wide variety of situations, such as React/Vue/Svelte state.

On the other hand, there are some downsides. The biggest one being that it’s only available via JavaScript, you can’t access it from Rust. The other negative aspect of it is the limitation of 3~5MB of storage (depends on the platform). This is a LOT of storage, but it’s not unlimited. If you need to store a lot of data, this is not the tool for it.

Wait a minute, is Local Storage truly persistent?

It is. The data is stored on the user’s computer, and it’s not lost when the app is closed. In the context of a Tauri app, it’s also not deleted if the user decides to clear their browser cache. The app is in total control of what gets stored in local storage and when to delete it. It’s possible for the user to go in there and delete the files manually, but the user would have to know what they’re doing and the consequences of doing so.

Local storage would be my first choice for storing App Preferences, but definitly not an option for User Data given the storage limit.

2. IndexedDB

This is somewhat similar to local storage, but it’s asynchronous and can store more data. It’s also available to JavaScript via the window.indexedDB object.

It’s a bit more cumbersome to use than local storage, mostly because it’s asynchronous, but also because of it being closer to an actual database, with support for indexes, tables, transactions and more. There is also less documentation available for it, it’s not as widely known and used as local storage.

In general I would not recommend IndexedDB for storing App Preferences, but it can be a good option for User Data. Although I’d personally prefer to use an actual database for User Data, such as SQLite, which we’ll cover soon.

3. tauri-plugin-store

Next up we have the tauri-plugin-store, which is the official plugin for storing persistent state in Tauri apps. It’s a wrapper around the file system, with an API available to JavaScript.

With this plugin we have a bit more control over where the data is stored, as well as being able to access it from Rust. The API has to be async due to JavaScript-Rust IPC, but it’s still pretty simple to use.

import { Store } from "tauri-plugin-store-api";

const store = new Store(".settings.json");

await store.set("app-theme", "dark");
await store.save();

const theme = await store.get("app-theme");

The plugin will basically create a .settings.json file in the app’s data directory, which is different for each platform. But because it’s just a normal file system document, it’s not limited to 3~5MB of storage.

But wait! Just because there is no limit doesn’t mean you should store everything in there. This plugin is intended for storing App Preferences, not User Data. If you need to store a lot of data, you should use an actual database, which we’ll cover next.

4. tauri-plugin-sql

If you’ve worked with backend systems before you probably interacted with a SQL Database at some degree. tauri-plugin-sql is the official plugin for connecting to any SQL database in Tauri apps.

Unless you’re building a SQL client, you probably want to use SQLite. It’s a simple, file-based database that is easy to use and doesn’t require a server. It’s perfectly suited for storing data for desktop and mobile apps. While tauri-plugin-sql supports SQLite, you could also roll your own implementation using other Rust crates like explained in this article from Moonguard How to use local SQLite database with tauri, which has the added benefit of being in total control of the implementation.

By using SQLite you get all the benefits of an actual OLTP database, such as indexes, transactions, and more, right in your app. With the tauri-plugin-sql you can also also execute queries from both Rust and JavaScript, which is a huge benefit.

In my opinion this is well suited for storing both User Data and App Preferences, although I’d probably recommend it more for User Data.

Comparison

FeatureLocal StorageIndexedDBtauri-plugin-storetauri-plugin-sql
Simplicity✭✭✭✭✭✭✭✭✭✭✭
Read/Write from RustNoNoYesYes
Read/Write from JavaScriptYesYesYesYes
Async or SyncSyncAsyncAsyncAsync
Best forApp PreferencesUser DataApp PreferencesUser Data

Looking for my recommandation?

  • I use Local storage for UI preferences that are not critical; Things that the user wound’t mind losing if that happened;
  • I use tauri-plugin-store for Backend preferences that are somewhat critical, such as API keys, License Keys and other more sensitive data because I have more control over how andwhere the data is stored;
  • I use tauri-plugin-sql for all the long term and larger datasets because of the flexibility and power of a real database such as Sqlite;

I hope this post was helpful. If you have any questions or suggestions, feel free to reach out to me on Twitter, I’d love to hear from you!

See you on the next article 👋

Analytics for Tauri appsWithout compromising on privacy

Aptabase is a privacy-first analytics platform for Tauri apps. Get insights into your app's usage in minutes.

Learn more
Aptabase Dashboard Screenshot

Want to learn more?

Complete guide to logging with Tauri

Guide··TauriLogging

Complete guide to logging with Tauri

Logging is such an important part of any application. This post will show you how to use the official Tauri log plugin to log messages with loads of examples

Where to find log files on Tauri Apps

Tips··Tauri

Where to find log files on Tauri Apps

Building a tauri app and don't know where to find the logs? This short post will show you where to find the logs for your Tauri app.

Where would you prefer to host your data?

European UnionGermanyUnited StatesVirginia

We typically advise selecting the region nearest to the majority of your users' locations. However, if your user base is global and dispersed, opt for the region that is geographically closest to your own location.