Loading
Interviews with Experts Bonus 25 exercises
interview

Understanding Web Development with Jacob Paris

Jacob Paris, a Canadian web developer and digital nomad residing in Bansko, Bulgaria, opens up about his work on Remix.

The lack of access to user preferences and time zones in server rendering is a significant challenge. Jacob proposes a solution called "Client Hints" and a more comprehensive alternative.

Managing time zones is a significant hurdle in web development. Jacob emphasizes the importance of servers outputting all the necessary information for correct client rendering. This is especially crucial when users might have JavaScript disabled on their browsers.

Jacob and Kent also address the difficulties in teaching complex web development concepts. One solution considered is the packaging of some functionalities into libraries to make the learning curve less steep.

As a final note, mentions his role in curating the Moulten Newsletter, a resource for the Remix community. Jacob expresses his gratitude for community contributions and recommends that those interested connect with him on Twitter.

Resources

Loading interview

Transcript

00:00:00 Kent: Hello everybody. Hi Jacob. Thank you so much for coming.

00:00:04 Jacob: Yeah, thanks for having me.

00:00:06 Kent: So Jacob, if you haven't run into Jacob yet, then this is a treat for you. Jacob has been posting nonstop on his blog all about Remix and the stuff that he's working on. Jacob's currently working on a course of his own that is gonna be awesome. That dives into building a linear clone.

00:00:26 I'll let him talk about that a little bit more too. But Yeah, to just get things going on our conversation, Jacob, why don't you give us an intro to who you are?

00:00:36 Jacob: Yeah, thanks Kent. Yeah, so my name's Jacob Paris, from Canada originally. I've been working as a web developer for 5 or 6 years now. And the last 3 years of that have been working remotely as a digital nomad.

00:00:51 Kent: Yeah, so where are you at right now?

00:00:54 Jacob: At the moment I'm in Bansko Bulgaria, which is my primary home base for most of the last 3 years. I'm only allowed 90 days at a time due to visa restrictions. So I'm usually here for 3 months and then I bounce out somewhere else. And when my time up is there, I get to come back.

00:01:13 Kent: It's pretty cool. So Of all the places you've been, what's your favorite?

00:01:18 Jacob: It's got to be either here in Bulgaria or in Thailand. There's 1 really good co-working space in Koh Lanta, Thailand that I'll probably be heading back to at the end of this year. And yeah, I just had a fantastic time there. So many great people, lots of other remote workers, and

00:01:39 it's nice to be super close to the beach and just be able to go out and chill out when I get a little tired of staring at the screen all day.

00:01:49 Kent: You don't get tired of staring at screens, come on. Just kidding.

00:01:53 Jacob: You gotta look out the window every once in a while at least.

00:01:56 Kent: Yeah, yeah. Go touch grass as they say. Yeah, so great. Well, Jacob, I wanted to have you on here because in the Epic stack, I borrowed 1 of your ideas from 1 of your blog posts. I'm pretty sure I borrowed a couple other things, but the thing that comes to mind is the way that we do client hints.

00:02:16 Now in Epic Web, we don't have time to get into client hints. There's not really a good place to put in an exercise for that. And so I am planning on doing like a tutorial or something. So anybody who's watching this now, like just go look in the tutorials. I'll probably have something in there about kind hints. In fact, I actually do right now, but I'll have something a little bit more formal.

00:02:38 And yeah, so the idea here is there's a limitation on our ability to server render some stuff. And Jacob, I'll let you kind of describe the solution, but I'll describe the problem first. So the limitation is we don't know what the theme preference for

00:02:58 a user is, like whether they prefer light mode or dark mode. And if they haven't set their preference, then we just have to guess. And that can lead to some problems. We also don't necessarily know their time zone preferences. We might know their locale or their language preferences. There are some headers for stuff like that. But yeah, the

00:03:18 browser just by default doesn't send all of the user's preferences, which some of which we might kind of depend on. And so there is a pseudo standard that's sort of being worked on to help with this called client hints, but that is, I mean, just like with standards, you never really know when that's

00:03:38 gonna happen. And even when it does, you don't know how many of your users are gonna have a browser that supports them. And Even with that in mind still, the client hints standard is a little bit limited compared to what we are able to do with the solution you have. So with all of that as the backdrop for

00:03:59 the problem, Jacob, can you talk about the solution that you came up with?

00:04:04 Jacob: Yeah, yeah. So it really is a tricky problem. The main reason that dealing with the time zones is so difficult is you can't actually predict whether the user is going to be able to patch any of this on their client. So if JavaScript hasn't loaded, or if maybe

00:04:24 they have JavaScript disabled, there's going to come a time when either the JavaScript loads and you can kick in your client-side fallovers in order to render the client-side information. Or maybe that just never happens. And a user who doesn't have

00:04:44 JavaScript loaded, they have to rely strictly on the HTML that the server sends. So we need to come up with a solution that is going to let the server output everything required for the client to be able to...

00:04:58 Kent: Yeah, and not only that, but like if you make a guess and then the client comes in and says, oh, actually this is the time zone they're in, or this is their color preference, the user is going to see that. They'll see the wrong guess first, and then it'll flash over to the proper thing, which is just a terrible user experience. And it's

00:05:18 the reason that we get hydration warnings.

00:05:23 Jacob: Yeah, so I mean, the ideal situation here would be if the browser could send that information up front. So if every time the browser makes a request you could send, say, the time zone or the, you know, whether they want light mode or dark mode or whether they have JavaScript enabled

00:05:43 or any information like that that the server can use to form a more opinionated server-side vendor, then that would be great. But our options in JavaScript itself are quite limited. If you're doing like a single-page application style where every request is coming

00:06:03 through something like Fetch, then you might be able to add those as headers to your Fetch client and the server would have access that way. But for proper document requests, like you're just loading a page, you're navigating directly in the browser or clicking a link from some other page, that's going to be a document request from the browser and the only

00:06:23 way to get that kind of information to the server is to send it in a cookie. So it's enough to set cookies on the client but the, you know, clients who have never been to your page before, they're not going to have those cookies set. And that's kind of the core of the issue that we really need to start, come up with a solution here

00:06:43 where a brand new user is going to get this information set without seeing any flashes of unstyled content, without seeing just the wrong configuration or anything like that.

00:06:58 Kent: Yeah, so how do you get those cookies set then?

00:07:00 Jacob: So the method I came up with after trying a few options was to sort of intercept the initial request from the user. So if the user makes a request and it doesn't have that information available, rather than sending them the

00:07:20 whole document with the unstyled version of the page, we just inject a little script, just a tiny little JavaScript snippet, that will set the cookie and then refresh the page. And that will automatically cause the user to, you know, to make the other request

00:07:40 and then get the correct information. Usually this means all of a sudden their dark mode is working, their time zone information is in there if you have that all available. As a fallback, there is a 0 JavaScript solution you can use where you use the actual refresh headers for

00:08:04 the service injects the JavaScript snippet with refresh headers that causes the browser to automatically refresh and reload. And that's a nice little fallback, so if the user doesn't have JavaScript enabled, that they don't get stuck on that page forever.

00:08:20 Kent: Yeah, that makes sense.

00:08:21 Jacob: Or can stop sending them that little snippet and ideally everything should move smoothly from then on.

00:08:29 Kent: Yeah, yeah, Thank you for that. So when I was working on the same problem, on my personal website it actually still works this way. I have this really janky, hacky thing that I'm doing to make this work where I have some theme components that will render an element, a DOM element that I call

00:08:50 like theme something. Like I think I've got like theme light and theme dark and these renders HTML elements and before it hydrates it removes the ones that are not in the right spot. Like it's a disaster. And so like it works well, but yeah, it's not my favorite for sure. And so when I saw

00:09:10 your approach, I took it and added it to the Epic stack. And the change that I made was, I actually include that script on every page, regardless of whether you've already set things. And then I just check whether what is in the cookie is the same as what we're expecting

00:09:30 it to be. And if it's not, then I'll update it in the cookie and trigger the refresh. And so this will come with the benefit of, if they have JavaScript disabled, that doesn't run at all, and they'll still get the full page, and it'll just have the fallbacks, and that's fine. Like, you have defaults, and then, you know. If somebody's disabled JavaScript, they're expecting the web to work poorly

00:09:50 for them anyway. So, but yeah, I think it's a really nice idea. And in fact, it actually, it resembles the way that the standard is planning on doing things where the browser sends a request and the server sends a response back that says, hey, I actually need some more information. So then the browser refreshes effectively

00:10:11 and sends the request with the information. But the nice thing about this is because it's a cookie, Now that only happens once. And now we don't have to have the browser get a response that says, hey, I need more information. Like every time that information is needed. So even when the standards implemented, I'm pretty sure we're gonna wanna keep

00:10:31 this approach around, which is good.

00:10:35 Jacob: Yeah, it gives us more power if there's any additional features we want to pack in there. I've seen some people, I don't usually recommend it or do it in my own designs, but some people have separate versions of their UI for mobile or for desktop that, you know, depending on the screen size, you may not want to send all those extra elements to

00:10:55 the browser if you have a dramatically different UI, depending on their screen size. So you could put some updated, you know, like some hints about the screen width here, and then your server could use that information to render 1 UI or the other. That's really powerful.

00:11:13 Kent: Yeah. You know, this actually reminds me, YouTube does something kind of similar to this as well, except very poorly, I think. But if you go to YouTube, and let's say that you're in dark mode, and so then you'll see YouTube in dark mode and everything, then close YouTube, change to light mode, and go to YouTube

00:11:34 again, and maybe throttle your network so you can observe this easier. But even on a relatively fast network, you can see this, where it will actually show you the dark mode, and then it will trigger a refresh. And I think actually in the query string, it will say like theme equals light or whatever, which is super weird. So YouTube is doing the same sort of thing of triggering a full

00:11:54 page refresh, but we do it better. I'm sure they have some constraints and old code, Like they've been around for longer than us. So we have the benefit of hindsight, but yeah. So that was 1 thing that I borrowed from your blog, but your blog has got just an enormous wealth of material on Remix. Is there anything else that

00:12:14 you like find yourself referencing a lot when people ask questions?

00:12:20 Jacob: Yeah, probably the biggest 1 is the guide I did on how to solve hydration errors in Remix. And that's helped folks working with Next.js as well because they're, you know, it's a slightly different subset of problems based on the way they hydrate the DOM, but it's the same, it's the same class of problems.

00:12:41 And so I built that as sort of a step-by-step guide where, hey, if you're running into these issues, start from the top of the guide, work all the way down, and you might find your case here. Maybe it's a Chrome extension that's causing it. 1 guy had his corporate antivirus, which wasn't even part of the browser, And that was

00:13:01 affecting modifying the page. So he would open it up in incognito mode and nothing there would, like everything would be broken there as well. And there's no way I would have thought to debug that. But eventually he solved it himself. So I'm like, I need to add this to the guide just to be sure no 1 else.

00:13:20 Kent: That's amazing. That's very interesting. So can you tell us why, like, I see this all the time, even from people who are producing content around this, that the solution is just suppress hydration error on the document or on the HTML element, which certainly does

00:13:40 suppress the hydration error. But why is that not just the solution? Why can your blog post not just be like 1 code example that shows that?

00:13:48 Jacob: Yeah, so I mean it does exactly what it says it's doing. If you're suppressing the warning, you're suppressing the warning. But that's kind of like treating an illness by not going to the doctor. It doesn't actually solve the underlying issue. So I guess for context, anyone who doesn't know, hydration errors are caused by a mismatch

00:14:09 between what the browser is currently displaying in its HTML and what React is trying to output as HTML on its first render. So when you are dealing with any sort of server-side rendering, the server is going to send a bunch of HTML to the client, and then the browser needs to

00:14:29 render all of React, and ideally, if everything works out, it should come up with the exact same result. And if that happens, it can swap out the server-side elements with the React-generated elements, and now they're interactive, and you have the full power of React behind it all.

00:14:49 If there's any mismatch there, which can happen for a number of reasons, you get just what's loosely called a hydration error. There are so many reasons this can be wrong. 1 is if your browser is relying on any information that the server doesn't have, like your time zones or

00:15:12 your dark mode preference, anything like that. So the browser has set it to a value, but the server set it to something else. It could be screen size related if you're relying on that. If you're trying to render based on something from local storage, that's going to cause an issue. But sometimes it's even a little more abstract than that, you can have Chrome extensions

00:15:32 that modify the HTML of the page before React has a chance to render. And then when React tries to compare its output with what's currently being displayed by the browser, that doesn't match up anymore. So Notoriously, there's a Chrome extension called Honey, which finds coupon codes for you. There's 1, the Grammarly has

00:15:53 a Chrome extension that modifies your inputs to spell check things. Those are notorious for causing issues with this. And they tend to break React apps all across the web. Just because of this issue here.

00:16:07 Kent: Yeah, I think that it's important to mention a couple of things about this. So anytime I talk or hear about hydration, people are gonna say, well, that's why you shouldn't hydrate your apps, right? And so like Astro and these islands, and if it's not interactive, don't even hydrate it. Or like Quick

00:16:27 and its resumability. But those will have these same problems. Like the idea of it will be something different on the server and the client. If it's something different, then the client's probably right and the server was wrong. And so if you don't hydrate and you're just doing resumability or something, then what the user's looking at is probably wrong. And so like that is going to be a problem regardless

00:16:48 of the framework or whether hydration is an issue. And then the other aspect of it, the reason that you don't wanna just ignore the warning is because when the hydration happens, the user's gonna see that flash of incorrect content And React is actually gonna behave slightly differently. So when hydration is happening, React isn't

00:17:08 changing the elements that were rendered by the server, that would be inefficient. So it's just using those elements and hooking up event handlers and things. But once it hits a difference, then it's like, whoa, okay, this is different from what I expected. I can't just attach event handlers. And so it has to re-render the whole page, which is also very inefficient. And so,

00:17:28 yeah, we definitely don't want to just ignore these issues. But at the same time, you don't get to control the extensions that your users have. And so if that ends up being the problem, then the user's just gonna have that issue on every React site that they use. And that is just

00:17:49 the way of it. And that it's not the end of the world, but it's certainly something you want to avoid for avoidable issues like time zones and stuff like that.

00:18:00 Jacob: Yeah, and it can cause a fairly slow page load experience as well. Because if React is running, or sorry, if Remix is running all of your loaders, fetching all the data it needs, server-side rendering HTML, and shipping that to the client. And then the client is in the middle of trying to display this when it encounters a hydration error and it says oh no Now we need

00:18:20 to re-render client-side. It then needs to re-run those loaders again to fetch the data to load To send the JSON down to the client and render that client-side So you end up double dipping into your database and the server side of things as well.

00:18:38 Kent: Certainly inefficient and something you wanna deal with and not just suppress the warning. So yeah, certainly not on your HTML element, my goodness. There are occasions where suppressing the warning might make sense. Like if you've got a countdown timer or something, so you server render it, and it's most certainly going to

00:18:58 be different on the client, that would be like, suppress it on the little time element that you're rendering in it. And that's it, like, I can understand that, that's totally reasonable. So there's a reason that suppression thing exists, but it's not for your HTML element, good grief. Okay, yeah, so there are other things that you've written

00:19:18 about and other things that you're working on with your linear clone course. So let's hear about that a little bit.

00:19:26 Jacob: Yeah, so the kind of the idea behind this course was to demonstrate how to build like some like nice optimistic UI into your apps because I I've looked at so many courses out there and they tend to give a Sort of a surface level knowledge like you learn the basic the basic CRUD operations

00:19:46 that you can do. You learn to add, create, remove, update, delete data. But then they usually abandon you there. Anything related to like a nice user experience where you've got elements fading in and out, your optimistic UI, so you don't have to wait for the whole network boundary

00:20:07 in order to see the items that you've created, so they can just smoothly appear in there, anything related to multiplayer use cases, So you've got multiple users on a page, can you show them on there? Can you handle someone else updating the same element while it's on your page and

00:20:27 being able to react to that? So I was trying to find a good example project where I could build these out, play with these ideas and try to demonstrate how you could build this in an effective way. And Linear is 1 of the best user experiences

00:20:48 out of most of the modern enterprise apps. And at its core, it is just a CRUD system. Like you're creating issues, you're updating statuses on them, you've got a table of data. The ideas that are in that application are common to pretty much any enterprise software, like the vast majority of what the average developer is working on day to day at work,

00:21:08 is going to have a huge overlap with what's in there. So I sort of use that as a model project. And as I go through and as I build these, I keep running into issues where it's like there is no canonical solution, or at least I can't find good articles and resources

00:21:29 to solve these, so then I write them. And because of that, I've put out a ton of content over the past few months. Sometimes I end up going down weird rabbit holes where I'm trying to add icons to the app and all of a sudden I'm getting weird auto import issues from every icon library out there and freak out about it

00:21:49 and decide to go straight to SVG sprites. And then I built that little Sly CLI tool for importing SVGs into your project. And I was able to contribute that backup stream to the Epic stack. So now everyone else can use that as well.

00:22:06 Kent: Yeah, and that's fabulous. We don't actually dive into that in the workshop, but we are definitely using it. So as you're going through all of this, all the icons are on the page, thanks to the Sly CLI that Jacob built.

00:22:19 Jacob: So 1 thing I've been having a little bit of trouble sort of reconciling is like I keep building things and then I'm thinking to myself, how would I teach this? How would I teach this in a course? And some things I think are just a little too complicated to properly, you know, to properly teach and communicate. Like 1 that I was working on

00:22:39 is having an interactive filter toolbar where you can click and you can you can choose any parameter for these issues. So based on status or priority it can equal any of the you know proper statuses or not equal. It can be greater than dates, it can be less than dates, and you have all these different parameters that are going to combine together

00:22:59 and modify how you query your database. So I start building these out but it's some issues are surfacing like this needs to be a library that people use. Trying to teach everyone to reimplement and rebuild this themselves is I mean it's just not feasible but it's also it's kind of a waste of everyone's time.

00:23:20 Not everyone needs to know how to build all of this stuff. And that sends me on further rabbit holes where I take a little break from the course and now I'm building out libraries and I'm not sure if I'm ever gonna finish this thing, but

00:23:36 Kent: You know Jacob that that sounds so familiar I Think I've told the story several times in the past but Testing library exists for that exact reason. And so, yes, and in fact, like I'm working on the web auth workshop right now, and there's a

00:23:56 section in the web auth workshop where we do 2 factor authentication and that requires generating 1 time passwords. And yeah, built a library for that because I didn't want to teach people how to do that because as valuable as that information would be, and it would, it would be very interesting, just use the library and move on with your life.

00:24:17 So, yes, many libraries have been created in the process of deciding, I don't want to teach this, I just want them to use it.

00:24:26 Jacob: Yeah, and there's been a lot of people asking in your Discord, like, why is the Epic Stack all this code? Why, you know, how do I pull upstream dependencies down? Why can't I just install the Epic Stack and, you know, subscribe to updates? And I think especially with your,

00:24:47 now you've got Epic Web TOTP, is a good example of that starting to happen where as you build up the features, they become a little more feature complete and you turn them into libraries, people will just be able to install them and use them whether they're using the Epic stack or not. They'll still be able to take advantage, but now they can automatically update and

00:25:07 use them in whichever projects they want. I think that's a fantastic way to take the project.

00:25:12 Kent: Yeah, that's exactly what the goal is there. Yeah, totally. So there are definitely some libraries sitting around in the Epic stack right now. Like for example, what we were talking about at the very start with the client hints, that is a library that will happen eventually for sure. Well, awesome. So

00:25:32 is there anything else that you wanted to talk about as we kind of get toward the end of our chat here?

00:25:39 Jacob: I guess I should give a shout out to the Molten Newsletter.

00:25:43 Kent: Oh, yes, yes.

00:25:45 Jacob: Yeah, so that's, it was a project started a couple years ago actually for to be kind of the unofficial remix community newsletter showing what everyone is working on in the community and you know including all the You know all the tips and tricks the latest articles You know YouTube videos

00:26:05 that are coming out, any relevant courses, as well as any meetups, conferences, just anything to do with people who are building cool things with Remix. And as of last month, I've had the opportunity to take over the responsibility of curating and writing that newsletter.

00:26:26 So, yeah, I guess for anyone, if you want to stay up to date on all the cool new things that are coming out with Remix. That's, yeah, that'll be the place to subscribe and take a look at.

00:26:39 Kent: Yeah, that is a great newsletter. I've been subscribed since day 1. Super jazzed that Jacob has taken the mantle of Moulton. And yeah, thank you so much for all that you do for the Remix community. You've just been super duper helpful and I'm looking forward to hearing more about your course in the future.

00:26:59 Jacob: Yeah, I really appreciate all the help you've done. I wouldn't be the developer I am today if it weren't for all of everything you've been teaching here.

00:27:07 Kent: Oh well thank you Jacob, awesome. All right, what's the best place for people to connect with you?

00:27:13 Jacob: That'll definitely be Twitter. So I'm at Jacob M Paris on Twitter and I tweet a lot there You'll probably run into me if you don't if you don't search me directly. So,

00:27:24 Kent: okay. Yeah, cool. What wait, what's Twitter? No, just kidding Everybody knows everybody knows That's a inside joke that everybody's on. So hey, thanks again, and yeah, we'll connect with you later.

00:27:41 Jacob: Yeah, thanks for having me, Kent. Have a

00:27:43 Kent: great day. Bye.