Understanding useState in Nuxt 3

Let me explain to you what useState is for and why it’s useful to us.

Michael Thiessen
Nuxt 3

Mastering Nuxt 3 course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view the Nuxt 3 course

I was confused for the longest time about useState.

It seems like it’s an important composable that I should be using in my Nuxt 3 app, but I wasn’t quite sure about how to use it, and what it’s meant for.

So, let me explain to you what useState is for, and why it’s useful to us.

Two Main Use Cases

There are two main uses cases for useState, one for client-side and other for server-side:

  1. Client-side → sharing state between multiple components
  2. Server-side → sharing state from server to client

This first use case is the most obvious, and the one which most of us understand the easiest.

The second use case, however, will require a bit more explaining.

Let’s start with the client-side though, just to get it out of the way.

Client-Side: Sharing state between multiple components

Sharing state across a Vue app has been one of the biggest challenges since the dawn of time.

Well, the dawn of Vue and other component-based frameworks, at least.

There are many good solutions already out there for this:

  • Pinia → the recommended solution to use with Nuxt 3
  • Vuex → a great state management library that still works well
  • and dozens more

The useState composable is not exactly a whole state management library though. It let’s us share state using a simple key-based system:

// In one component
const sharedState = useState('share-this', 'default value');

// In another component
const alsoSharedState = useState('share-this');

As long as we use the same key, the refs returned by useState will be the same. Easy, right?

The point of useState here is in sharing simple state. If you need anything more complex, you should use Pinia instead.

This is what we do in Mastering Nuxt 3. We start out with using useState for some basic state, but then graduate into using Pinia as things get more complicated.

Now, let’s move on to the more complicated server-side use case.

Server-side: Sharing state from server to client

One of the main features of Nuxt 3 is that it’s server-rendered.

Instead of sending just a bunch of Javascript and requiring your browser to do all of the heavy-lifting, the server renders out the page and sends the HTML (and a smaller bundle of Javascript).

There’s a lot more nuance to how this works with Nuxt 3, but server-rendering like this is much faster than the equivalent single-page app that relies on just the browser.

However, server-side rendering also comes with it’s fair share of challenges.

State hydration

The first challenge is state hydration.

The server runs all of the setup code, then renders the page. It saves the entire state of the app so we can boot it up instantly on the client. This process of booting up the client with the saved app state is called hydration.

By default, Vue doesn’t know how to handle this.

If we’re using ref or reactive to store our state, they don’t get saved and passed along. So when the client is booted up, they don’t have any value and we need to rerun our setup code on the client.

Normally, this is fine. It only becomes an issue when your ref relies on state from the server, such as a header from the request, or data fetched during the server-rendering process.

That’s where useState comes in, because it’s designed specifically to handle state hydration like this. When Nuxt 3 renders a page, it will store all the state being used from useState and send it along to the client. Then, when the client boots up, it will hydrate that state back in, jump starting every piece of state that was using useState.

Cross-request state pollution

The second challenge is what’s known as cross-request state pollution.

If multiple people all visit your website at the same time, and those pages are all first rendered on the server, that means that the same code is rendering out pages for different people at the same time.

This creates the opportunity for the state from one request to accidentally get mixed up in another request.

This can become a very bad problem for us! It poses a huge security risk and the potential for data leaks.

But useState takes care of this for us by creating an entirely new state object for each request, in order to prevent this from happening. Pinia does this too, which is why it’s also a recommended state management solution.

Again, useState is not a state management library, and is best used for simpler cases. If you need anything more complex, that’s when you should turn to Pinia!

I hope that this gets you excited to sign up for the upcoming Mastering Nuxt 3 course that I am putting together. Join up there to qualify for the big Early Bird that will only be available for a few hours when the course is launched this Fall.

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on