Understanding the Order of JavaScript Module Evaluation on the Web

Kent C. Dodds
Kent C. Dodds

In this video, we explore the order of operations for evaluating JavaScript modules on the web. We go through the different files and console logs to understand when and where each module gets evaluated. From the server index to the entry server and client, we trace the flow of evaluation. We also discuss the evaluation of scripts and the difference between server-side and client-side evaluation. If you're interested in understanding the order of JavaScript module evaluation, this video is for you.

To learn about another inline script use case, watch Use Client Hints to Eliminate Content Layout Shift.


00:00 I want to show you the order of operations with regard to when your different JavaScript files get evaluated. So what we're going to do is add a bunch of console logs in this application. I'm going to start in the server index JS or TS here, and I'm going to add a console.log server index TS. We'll put one right there. And then we're gonna go to our entry server and we'll add a console log here, console.log entry server TSX.

00:31 There we go. And we'll add another one in handle request. So console.log and yep, entry server TSX handle request. And then let's add one to entry client, console.log entry.client TSX. And we'll stick one in the start transition as well, why not?

00:51 And right here, start transition, and then in root TSX, let's also add one here. We'll put it at the root of the root, so console.log root TSX, and then we'll add one console.log app in root TSX. All right, so with all of those logs in place, let's also take a look at the logs that we've got right here. And to clear all this stuff up, I'm actually going to restart the server here. And that way we can see from the very beginning, where are all these logs.

01:25 So right here, we're getting the debugger connected and all that stuff that's happening as part of our dev server we're running with the inspect flag. And so that's where the debug server thing is coming from. And then it actually evaluates our server index TS. So that's the first thing, like your first entry into any of the code that you're writing is going to be inside this index ts. We don't typically make a lot of changes to this file, but yeah, if you need something that happens early on as the server is starting, then that's where you put it.

01:59 If you want something that happens early on in handling of remix requests and stuff, then that's all happening right here. So that is like the earliest thing that your app is going to be doing. Then we have our entry.server.ts logged right there. So this is the first of your remix app server files that are going to be evaluated. And so if you need something that happens really early on, like our global.env being set right here, then this is where you're gonna put that.

02:31 And then our root TSX is gonna get evaluated. So that's gonna bring in or import this file. And so that's where we end up with this console log of the root TSX. That's just happening as a natural process of importing the root TSX file or the file that has that as kind of the bundled portion of our app. And then once all of those imports have happened and everything, then we start our server.

03:00 So this really all is just starting thanks to the fact that we're importing our build. So right here this import of our build path that is triggering all of the root level console logs to happen because we're evaluating all those modules. So that's what's going on there. Once our server has started, then our first request is being handled here. So we're making a head request.

03:24 That's just to say, hey, are you up? Are you ready to go? And so this handle request is going to be evaluated because that head request is being made. And so if we take a look at our entry server here, that's where that's happening. So we're handling a document request there.

03:45 That is the HTTP method of head. And then app in root TSX, that's happening because Remix server is being rendered and that is going to render our default export from our root.tsx. And so that's why we're getting this console log on the server. Okay, so that's all what's going on on the server side of things. On the client side of things, we're not seeing anything on the server with the entry.client.

04:14 That's because it's happening in the client. Nothing in the start transition. That also is happening in the client. So that's where we're not seeing any of those things. But if we pull this over and pull up our dev tools and we can see right here, let's actually do a hard reload so we can see all those logs.

04:31 So we're gonna see a root TSX. So that's actually the same as what we had with the server side. We have this root TSX here as well. So this is being evaluated both on the client and the server and That's just by virtue of the fact that it's being imported at all. And then if we go to our entry client, we're going to have the console log for the entry.client right here.

04:55 And the start transition is happening there. And then as part of the hydration of the document with the Remix Browser component, we're going to take our root TSX default export, which we call app, and we'll get a console log for that. This is during the hydration process. And then finally, we get the dev tools is initialized, that is coming from this import of the dev tools here. So that's what's going on right there.

05:24 And that's happening later because this is a dynamic import. So that's gonna be on a future tick of the event loop after our hydration is finished most likely. So that is the order of things. Now there's one other thing that I think you might be interested in understanding a little bit if that could be helpful to you. And that is right over here where we have this dangerously set innerHTML, when do these scripts evaluate?

05:54 And where do they evaluate? So for one thing, the server is not going to evaluate any of the script tags that you put in here at all. These are going to be just rendered as HTML. They will not be evaluated. That said, there is some evaluation going on here because we're dynamically generating this HTML.

06:13 And so this HTML right here, or this portion of the HTML, is going to be evaluated as part of the server render. So on the server, we're getting this app in root TSX. When that happens is when we're going to be evaluating this value for the server. But the actual execution of window.env, that's not going to happen until it gets into the client. And so all that we really get is the creation of the inner HTML of the script tag.

06:43 So if I look at view source and we say window.env right here, we're seeing mode development. It's just this part right here that was dynamically generated as part of the evaluation on the server. We're not gonna get any actual assignment to window at this point. Where we do get that though is on the client. So let's add a little bit more to this so we can see that.

07:07 So we'll say console.log, let's yeah, root TSX script, that works. And we'll refresh here. And right here, we're gonna see root TSX script And that's happening even before this module is imported. Now, that's very interesting, right? Because you think, hold on a second.

07:25 Doesn't the root TSX need to be evaluated before this script is evaluated? And it technically is, because it's running on the server. So we're getting that root TSX right there, and then we get the app in root TSX there. But again, this is not being evaluated at all on the server side. This is only a client-side thing.

07:46 So we are generating the value on the server, but we're not actually evaluating that string value until it hits the client. So what's happening here is if we take a look at, actually might be easier to look at the DOM output here. Whoops, scooch that over, and then scooch this over. So if we take a look at our DOM, we'll see we have this inline script that's for our scroll restoration that's happening right there. And then this inline script is our console log.

08:18 And then we get a bunch of module preloads. This is so that we can have the browser get all of these modules in the browser cache so that they can be evaluated when they're imported. We have another script here. This is for the remix context with all the data for hydration. And then we have this module right here and another module here.

08:38 This one is for the KCD workshop, so you can ignore that one. So this one is where our application starts. And so the reason that we're getting this console log before our console log that shows up inside of the root file is because as far as the browser is concerned this script tag shows up before we start to import anything. And so if you need something that evaluates on the server as early as possible, you can put that in your server TS file or you can put that in your entry.server.tsx file. But if you need something that evaluates as early as possible on the client, then you probably should use the entry client file.

09:22 However, in our case with the environment variables, we need something evaluated even before the client is evaluated because we're using this env variable here. So it's not really common that you're gonna be doing inline scripts, but that is the most early possible thing that you can do for getting things evaluated really, really early on. Now, what's interesting is we actually also have some other use cases for this as well, and that's the client hints, which I will link in the description of this video so you can go understand what are client hints, and we're using the same sort of thing to make sure that we have the right cookies set for whatever the client preferences are. So like their theming preferences and stuff like that. So take a look at that video if you want to dive into that.

10:13 But that's another use case for doing this sort of thing of like a really, really early evaluation of some JavaScript on the client. So hopefully that is interesting and helpful to you to get an idea of all the different places where JavaScript is initially evaluated. And yeah, have a good time with all the fun stuff you're doing on the web, peace.

More Tips