Using cookies to share state between client and server components

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Next.js Complex State Management Patterns with RSC course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Next.js Complex State Management Patterns with RSC, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Next.js Complex State Management Patterns with RSC
  • [00:00 - 01:37] Welcome to lesson three of this module. On this video, we're going to cover a very essential aspect of web development cookies. We're going to talk about cookie management, we're going to see some best practices and most importantly, we are going to learn how to use cookies to share stay between plain and server components. So let's go. So the first thing that we need to answer here is what problem are we solving? On the previous lesson, we talked about using props to share state that originates on the server and needs to travel to the client side. However, here we're talking about another part. We're trying to solve persistent state in a way that makes it easy for us to access it either on the client environment or on the server environment. Essentially, we need a ubiquitous type of storage that is always accessible through either the client or the server. And this is why something like a database would not work because while it's definitely accessible on the server side, it's usually not accessible or at least not a great practice to access it directly from the client side. So how do we find a place or where could we really store our state in a way that is directly accessible from either environment? And that is where cookies come in. To understand what cookies are, essentially, is cookies are data stored on the client's browser that travels back and forth on every request and response, essentially. So that is the key. That is what makes them ubiquitous. That is what makes them available directly available to both environment because they are part of every request and response that we perform.

    [01:38 - 01:48] The client will always have access to the cookies because the cookies are lived there, essentially. That is where the browser will store them. And it will always send them as per other requests.

    [01:49 - 02:55] So the server on the on its side, we always have access to it because it can access the request data. That is kind of the mechanic that we're going to exploit, if you will, if you want to use that word, to use it to store our state data, at least the information that we definitely need both environments to access immediately. And for that, I want to show you two sample sections of the application that deal with the outflow because the the authentication flow of an application usually deals heavily with cookies. And in this application, I did also implement authentication using cookies. So I'm going to show you how I'm using that storage to store that particular piece of state, essentially. The fact that the user is logged in, that is a very important piece of state that the entire application should have access to. So the first thing that I'd like to show you is the form, the login form component. Remember that the login form component is a very simple one. It shows you two fields. And you can toggle it into between a login form and a signup form.

    [02:56 - 04:03] We're going to care about that particular case right now. Just we want to see what happens when we click on the login button. And what I want to focus on is this the handle login function. This function gets executed whenever you click on the login button. And what it does is it will perform a fetch request to our login endpoint. And I'll show it to you in a minute. And it will be a post request. It will send the user and the password information essentially. And then it will get the response back. If the response has an error, then it will show an error. Otherwise, it will perform a redirect to the list essentially. The list section is a protected page in the sense that you can only access it if you're logged in. So the login form is the one that gets to redirect you there if we get a successful response from the backend. So that is all really, we're not dealing here with cookies, but look at what happens on the server side.

    [04:04 - 04:42] This is your standard API endpoint for next using the app router. We will actually cover this structure in the next video. So if you've never seen it before, don't worry about it. I'll cover it on the next video. But right now what you have to worry about or at least pay attention to is the fact that we're exposing a post endpoint right for this particular route. And we are getting the user name and the password that we shared through the request. We're just calling a login function. It doesn't really matter what it is. It's just querying the database and doing some operations.

    [04:43 - 05:08] It doesn't really matter. But if there is no user file, we respond with that message. There is either the user name or the password. I'm correct. We don't really care. However, if there is no problem and if we've actually found a user, what we're going to do is we're going to create a cookie called loved. And we're going to save inside a cookie the user ID, the ID of the user in the database.

    [05:09 - 06:21] There is all the information that I want to store in the cookie at this stage. And that is, you know, the thing is, even though you can definitely secure your cookies and we're going to look into some best practices in a bit, you still have to think that cookies are data that gets said back and forth on your on your requests. So if you don't properly secure your terrier application through different techniques, there is a chance that a potential attacker might get a hold of those cookies and grab the information that is stored there. So remember that when you're thinking about what kind of information you want to store in the cookies right now here, I'm just I'm just storing a user ID, which translates to a number essentially can be anything to be honest. So I know my leadership knows how to use this particular piece of information piece of state to perform other operations, but you know, a potential attacker that gets a hold of it might not really know what to do with it. So it's really harmless in that way. But store information like the user password, for example, would not be a good idea . So again, I am setting a cookie here with the user ID. And that is really all that I do.

    [06:22 - 07:33] Notice that I'm using the cookies function, which gets imported from a next helper. Next gives you all the tools you need to deal with cookies on the back end. So this is definitely a very simple way of doing you don't need for the back end operations, you don't need any external libraries or anything. We will look into one for the front end though. And as you saw before, if we send back a response that has no errors, which is our case here, then the front end will assume that the cookie got created and it will create a task to the list component, a place where, you know, the cookies might be useful or not. But the point is, this endpoint did everything it needed to to create the cookie and then to store that state essentially in a place where every component of the application can access it. Now, let's look at how you will use dial information on the client. Let's look at how to use the cookies, then this state store in the cookies on the client side. And for this, I'm going to be using the JS cookie library.

    [07:34 - 08:19] This is a library that simplifies the whole dealing with cookies on the front end. Like it says here, child script is more the capable of directly doing cookie management and cookie operations. But it's not really straightforward and you know, you need to know a little bit more about how cookies are created and the specific syntax for it to properly work. So this library will simplify the whole process for us. So there is really no need to avoid it. Now, to show you how to use the cookies on the client, I want to show you the the alpha context that I created. This alpha context handles essentially any operations around the piece of information I just show you the logged cookie. That's all it does.

    [08:20 - 08:54] But you know, across your components, you will need to know the user ID potentially. You will need to know if the user is logged in or not. And you will need functions such as the logout function that destroys the cookie. You might want to centralize the kind of operations inside a context provider. In the case of us, we did that on the on the out context provider. And remember , enough provider or a context provider is just a component that can handle internal state. And that is what we're going to be doing. We're going to have an internal state called token.

    [08:55 - 10:07] And that variable is going to contain the actual user ID when the cookie exists . So what I do is on the moment the that my context provider gets mounted on the DOM, it will check for cookies using the proper name, the log cookie. And if there is something there, then it will grab that value and set it as part of its internal state. That is it. That's all we do right now. We are automatically grabbing the cookie whenever the page loads, essentially. And if the cookie exists, if it has something inside it, we'll grab that value and we'll store it in the internal state of this provider. Now, the provider, obviously, is sharing that internal state is sharing the functions called is locked, is locked, login and lookout. The login function allows you to set the cookie to whatever you want, essentially. If you want to, and then it's locked, it is a Boolean function that will return true if the token, if the internal state has a value, otherwise it will return false.

    [10:08 - 11:18] Essentially, if the user is locked in or not, if the cookie exists or not, then it will give you a true or false. And the log of function will remove the cookie and the internal state. Essentially, it's very simple. As you can see, it's just removing the cookie with the name and then just also clearing the internal state of the provider. So that affects the entire workflow of all the other functions and any other logic around this value. Now, another interesting point of consideration is that remember that state is just a value in memory in your browser. So if we were just to somehow pass the state using the login function or whatever other potential function that we might create and set that value from a client component, a simple refresh will clear that value. That is why we're also dependent on the cookie. We need to persist that state for two reasons why to make sure that the state is accessible, like I said before, from both environments, client and server. But we also need to make sure that a simple action such as refresh of the page or just navigating from one page to the other with a full refresh of the application doesn't kill our internal state.

    [11:19 - 13:08] And it actually does. But with this use effect hook, we're actually able to reload that state and make sure that the entire app has access to it again. So as I mentioned before, there are some best practices or at least guidelines that you might want to follow depending on your on the use you give cookies. When you create a cookie, there are some properties that you might want to give it. Making it secure, right? For example, a secure cookie ensures that the cookie is only sent to the server if it's through an HTTPS connection. With my example, I'm just using a local server. I'm not deploying it anywhere. So there is no HTTPS connection, a secure connection. But if you want to deploy into production, you will need a HTTPS certificate and you will need to make sure you know, you will need to make sure that the the communication between the server and the client is always secure. This ensures that there is no potential third party trying to fake a request into a non secure version of your server. The same site attribute controls whether you can share the cookie with the same with other sites or not. It is it is actually kind of a policy has several values where you can just let it do whatever you want. So you could potentially have a cookie that is sharing among different domains, different sites that make use of it. For our case, we want this information only accessible on our application. So we would set it to strict. The strict value only allows the browser to share the cookie in requests that access the same domain where the cookie was originated essentially. And finally , there is the HTTP only attribute that is good to keep in mind because it might have some potential use cases for you.

    [13:09 - 14:26] But in our case, it wouldn't be useful because it prevents client cycle, essentially from accessing and reading the content of the of the cookie. And in our case, we can't really have that because we need the client to read that and to and to make use of the information in the cookie. So while it's not useful for us, the HTTP only property of the cookies might make it useful whenever you have data that only needs to be on the server side. So in summary, cookies are a powerful tool for state persistence. They are crucial to authentication workflows as you've seen by any other workflows, any other piece of state that should be persisted and directly accessed by either the client or the server should be stored in a cookie. Now keep in mind, the cookie is sent as part of the headers of your requests. So it's not like you can put, you know, a lot of information there. But if you make smart use of that space, you can definitely save yourself a lot of trouble and improve the performance of the application by giving both environments a very direct access to the state. In the next and final video of this module, we're going to look at a third technique to share state between server and client components that is useful when cookies or props are just not enough.

    [14:27 - 14:29] So until then keep coding and I'll see you in the next one.