Transcript
Hello. Welcome to this week's Vercel community live session. I'm Amy, I'm one of the community team here at Vercel and I'm going to be one of your moderators for today. If you don't see the chat, make sure you're signed in and click going on the event listing, and then you'll be able to see the chat. Before we begin, I just want to remind everyone of our community code of conduct. You can find that linked up above in the site nav and below in the site footer, and let's just continue to keep it a positive space for everyone so we can keep doing these events. If you feel comfortable sharing where you're listening from, you can let us know in the chat. We love to see where everyone is tuning in from. And of course, feel free to share any of your comments and questions. We'll save those and ask them at the end. And I'd like to hand it over to Archa.
Thanks, Amy. Hi, everyone. I'm Archa and I am part of the customer success team here at Vercel. I'm really excited to be here. It's an absolute pleasure to be part of this vibrant and growing community of developers, builders, and innovators. In my role, I'm here to ensure that you're getting the most value from Vercel, whether you are just starting out or already deploying at scale. I also want to give a quick shout out to our amazing community team. They are dedicated to supporting you with expert solutions and workarounds for any Vercel platform issues you might run into. Whether it is through personalised assistance or easy to access self-served content, they're always working to make your experience smoother and seamless. So, if you haven't already, I encourage you: get involved, ask questions, share your feedback, and connect with others. We are all here to grow together. Thank you so much for joining today's session.
All right, so super excited for this one. Joining us today is Jonathan Alford from Roboto Studio. He started the studio to cut through the noise of bloated agencies, focusing on speed, simplicity and modern web tech. His team builds top tier editorial experiences and was among the early adopters of AI tools like v0. Today he is diving into how they optimise their Vercel usage and workflows. So Jonathan, over to you.
Hey everybody, how's it going? So this is optimising usage on Vercel, and I want to talk to you about a couple of the different things that we do in house. And we'll start with the agenda. So first of all, let's kind of break this down into how we actually diagnose what's causing the overage, how we handle fetching, how we handle caching. When we talk about media, what do we actually do when we come to optimising it? And finally, we're going to use AI to essentially roll up everything we've learned today and put it into a set of actionable rules.
So, let's start from the first one, diagnosing. The first thing I'm going to talk about is observability. If you haven't used observability, it's an incredible tool, and you probably want to enable observability plus if you're on the enterprise package. Essentially you want to use it to tackle the low-hanging fruits. And let's talk about that. So if you're looking at this here, the first thing you want to notice is what can we actually tell from these graphs and what should we be looking at? And the first thing that stands out to me is the Vercel functions. So with the Vercel functions you see these gigantic spikes, and I see something that goes up to the very top, which is at 60%. So I know something is going wrong, and I know a lot of it is going wrong. I can also notice on the right hand side that there is a tremendous amount of consumed hours with very, very little saved hours. You will notice though, if you look very, very carefully at the bottom right one, progressively the saved has started kicking up, which is great news.
Anyway, so let's look at the first of those big spikes from the previous page. Now this is a particular slug that we can see has a tremendous number of invocations. So you can see all the ones below it. The highest one that's closest to it is 182K, whereas the actual top one itself is on 2.3 million. This should be ringing alarm bells. And as you can see, the further on we go down this, you can see that there is an error rate of 43.8%. The reason we have that error rate is because we've actually hit both the API usage of the external tool that's being used here, and by proxy, Vercel is getting basically error pages because of the way that this was initially built. So this is something that you need to fix fast, but also you need to work out where it's coming from and why it's happening.
And so with that I kind of want to talk about speed insights. And this comes back to the whole idea of how we actually optimise usage inside of Vercel. So the idea is that if you have an extremely performant website, chances are you use less data, and by proxy you're actually using far less of your, you know, your quota. So when we check speed insights, this is something that we always run on every single one of the websites or web applications when we first set up a Next environment. The reason being is because there's a generous amount of use inside of it. It takes one line to set up and it's very, very useful for breaking things down to see exactly what the problems are. So again, the thing that we're focusing on is the left hand side. We don't want to see the good scores. You know, it's nice to see the 98 out of 100, you know, give us a pat on the back. But the thing that we really want to pay attention to is the low scores. So, anything below 50 and anything between the 50 and 90 point is worth looking at. So, you usually pick the poorest, but we're going to go for the slightly better than that. So we're going for the middle one. We're going to go for blog.
Now, the way that we can read this, the way we can understand this, is by looking down the left hand side and essentially understanding which ones are the most problematic out of this. So first contentful paint's an interesting one. It can be a pain point because of the amount of data that's being fetched in the first instance of this, but it's not the worst. Let's just go from a very simple, you know, green, yellow, red perspective. Largest contentful paint is huge at 8 seconds. So there's definitely a big problem there. Interaction to next paint is pretty high still, but it's not, you know, enough to worry about. Cumulative layout shift shouldn't basically exist if we're using Next images and things like that correctly. And then again, first input delay is pretty poor and time to first byte is pretty poor. The one that concerns me the most on this is largest contentful paint, especially with it being a /blog route. The other thing to be aware of is the number of users that have actually looked at this. So if you look at the very bottom, that 368 with a little three dots next to it is the number of users that have essentially gone onto this page. And the one to the right of that is the performance of this page: 46. So with largest contentful paint, that probably means in this case, if we know it's on the /blog, there is a tremendous amount of data being loaded, and if there's a tremendous amount of data being loaded, the performance is terrible. So we want to optimise basically both of those things, the data and the performance. And so if you have something like a blog, the reason that this was happening is because there isn't pagination in place. So instead of, you know, loading a hundred or 200 or 300 blog posts, which is not a great experience and it's very slow, we can instead break it down and paginate those or do some sort of loading technique within that.
And now I want to talk about more of the bad actors. So far we've only really discussed good actors and the idea of looking at a website and understanding what is happening from the perspective of somebody wanting to use your website normally. But oftentimes it's actually not the normal usages that cause the problems, and I want to talk a little bit about DDoSing, you know, if there's a single IP or any of these sort of things that are in here. And I want to show you this. So this is the firewall inside of Vercel. I'm sure most people have actually seen this at some point, but I just want to very quickly talk about the idea, the whole DDoS mitigation and allowed requests. So you can see there's 1.4 million requests on this, and of those 1.4 million, 590 are DDoS mitigation. Now, with the number of users that are hitting this website, that's actually a pretty good score. However, there are times when you might catch one of these. You might catch where that red spike went up, because that red spike went on for probably, I think it was a half a day period. If that happens, that button at the top, the enable attack challenge mode, is a fantastic tool. Now we have enabled this before in the past and it immediately dropped basically all the DDoS attack straight away. The one thing to know about this though is the user experience isn't as good while it's on. So, you don't want to use it as a consistent fix. You want to use it as a temporary fix, and then you want to basically solve the issues that caused the usage to get blown out.
And so we'll talk a little bit about Vercel analytics when you want to actually track down what is happening. Now, this is a bit of an interesting tip, because not many people necessarily use it for this reason, but it is a very, very useful reason. And I want to show you two totally separate parts of the same list. So, this one on the left hand side is if you press the view all on the routes, and this is kind of like normal usage. I can see that the homepage has got quite a lot of people on it. This has some particular slug that has quite a lot of people on it. It has blogs, contacts, you know, all the usual things. And then on the right hand side we can see that events has two, and I've obviously scrolled down quite a way to see that all these routes are getting hit because they've only got like one or two visitors, but then I see, hold up, what is this "cy state", you know, what is "wait for delay"? These aren't pages, these are code, essentially, and I can see that somebody is trying to do some form of injection. So it's very helpful to know this, because I can see which routes they're hitting, what they're trying to ping, what they're trying to do, and essentially I can understand how people are trying to use our website in a, you know, in a bad actor way essentially.
And so with that being said, we've kind of talked about how we actually diagnose it. Let's talk about how we actually fetch the data and caching of that data, so we can kind of avoid these first problems from happening. So first of all I want to talk about parallel fetching, because parallel fetching happens quite a lot when you're building applications. There's a lot of times where you have data that you need for one part of the page and another part of the page, and you essentially do not want to go in a waterfall. So this is basically how you do it. This is the actual fetching itself. And what we can see here is that we have a posts promise, a users promise, a stats promise. These are three different data. They may be from the same data source, they may be from different data sources, but the most important thing here is that they all run parallel. So in other words, we don't have to wait for posts to finish for the users to start. We don't have to wait for the users to finish for the stats to start. It's very, very helpful, and it kind of ties in very nicely with how we actually handle this on the front end.
So on the front end, we're going to use multiple skeletal loaders for each component, and we're going to try and avoid this cumulative layout shift, because we're at the point now where cumulative layout shift shouldn't even be a thing. And very specifically I want to tell you a nice way of doing it with artificial intelligence. So you can see here we're using a little bit of Tailwind. If you haven't seen Tailwind before, this is essentially a set of columns. We have, sorry, two-thirds of columns on the left, one third of columns on the right, and you have all this information inside of it. And you can see how it's basically set up. Now we're using a suspense. So instead of having like a gigantic circle loader that sits in the centre of the screen, we want to have these skeletons that you've probably seen on, you know, Facebook and things like that, where they show some sort of gradient that's morphing to show that there is data being loaded in. Now at the time that all of these parallel data sets are, well, the data is coming in, we will load each individual section. So in other words, if posts come through first, we will load post section, or for example users, you will use users, and so you will actually get this essentially construct in real time on your page, and it's a very nice experience if done correctly.
I want to talk about the AI side. So the way that you would do this with AI that's really, really clever is you would basically feed in the real world posts that you get, maybe a screenshot, maybe the actual code of the posts, and you would basically just ask it to create a skeletal loadout with fixed widths and fixed heights. What this means is that when they're actually loading you get this nice gradient effect, and when the actual data actually is loaded and is put on the page there isn't any shifting. There's no cumulative layout shift. You just see the posts. And it's really, really nice when it's done properly.
And now I want to talk about try catch. So this is something that's really helpful if you are pulling data, and you should do this. This is a great experience for both developers and, essentially, debugging. So this is a generic type that is a way of being able to fetch data from somewhere and, essentially, if it fails, get an error back in an array. So essentially this is how it works. And you can see how it actually is implemented here. So you can see the post at the top with the post and the error, and you can see how it's awaiting the try catch. So it gets the post and it pulls the posts. Now this is a particular example inside of Sanity. We use a lot of Sanity to build websites and such, but this works for basically any type of data that you're pulling. And we have some examples at the end of specific ones for like page routes and things like that. And you can see how this actually returns a graceful error state. So you see here "could not load post", instead of getting just that giant full screen error, you know, 500, that scares clients basically. So if it works, you actually get this post display.
And now I want to talk a little bit about caching. Now we've talked about fetching, go to caching. This is an example of caching with something that has a lot of global calls to this particular data. So something like navigation, but like a significant mega nav. This is using the unstable cache with Next.js. You can see how essentially we have this data that's being pulled from a global navigation query, and inside of this we're essentially having the revalidation be for every hour. So at that hour point you're basically passing something on where it makes it faster. So rather than essentially waiting for this to be loaded on each page, which is very, very bad from a performance perspective and very bad from kind of like a usage perspective, this now will just be cached and it will keep rehitting it and rehitting it and rehitting it. Which is very useful, because ultimately it's the same thing that we've said all the way through: anytime you have good performance, chances are you have good optimised data usage.
And then finally, this one's a great one, and you can probably, you know, show your boss that you've built this, because you haven't. You've basically just turned on fluid compute, and the whole build process has probably got 100 milliseconds, 200 milliseconds faster. Fluid compute is fantastic for firing off all kinds of different bits and pieces. There's no negatives with the actual functionality as far as I'm concerned. It's literally just turn it on, it works, it's fantastic, and welcome to the future, you know? It's really, really cool.
Now we've talked about the data fetching and the caching. Let's talk about something that usually is the majority of data that comes from a website, which is media. So first of all, let's talk about images, because whenever we inherit one of these, any kind of project, usually this is the thing that people don't get correct. Next image is a fantastic tool and it does a lot of leg work for you, wherever you are pulling your images from. And very specifically I want to talk about this section. If you remember just one thing from this presentation, remember this one line of code. So line eight is the most important one. So where it says sizes, this is a perfect example of kind of a predefined image that you may want to use again and again. I've left out the props, but ultimately it's passing down data, and the sizes section is basically telling you how you want the image to be optimised. Now this is a little bit different from what you would usually read, but it's very, very similar to kind of Tailwind. If you've seen Tailwind before, it's basically saying that up until a 768 pixel screen, which just happens to be the Tailwind default for tablet, make the image 100% of the view width. It basically sits 100% the view width. And then after that point, after 768 pixels, what happens then? It goes to a maximum size of 512 pixels. Why is this important? Because essentially, if you are using a normal website or a web app, you typically only use half, roughly, of the container of what that image is in. So in other words, if you have a website, you have text on the left hand side, some rich text beneath it, some, you know, bullet points and things. And then usually it's a 50-50 split. On the right hand side, there's a nice image. And usually the container is somewhere between 1,024. You could bump this number up if it's a bit bigger than 1,024. If it's 1,200, you would change this to 600 pixels instead of 512. But ultimately you're getting a nice level of optimisation without scaling the image far bigger than it needs to be. You don't need a 4K image for 512 pixels by 512 pixels.
And then the other thing that's worth mentioning on here is lines six and seven. The reason why the width and height is so important in here, and the reason why we kind of got these fallbacks in place, is because that is the thing that dictates the size of the image. And that's important because of cumulative layout shift. Cumulative layout shift is, and I guarantee you've experienced this, when you go onto a website, you go to press something, and then ultimately the page loads a little bit more, and then you end up pressing an ad or something like that, and then you end up going to it, and it's just a horrible experience. It shifts the page about. We don't want that. So, if you don't know how to build these things, definitely just use this image. Tailor it to how you would use it and reuse it again and again, and you're going to leverage the best out of Next images.
And this is an actual global based one. So this is a Next config, rather than doing it on an image by image basis. So there are some circumstances where you might have, for example, an older client base that doesn't need, you know, super high DPI images. We can go with much lower. So the device sizes might actually be far smaller. Same principle: if you know that it's only going to be a mobile only application, you might want to just drop the device sizes to lower. The other thing that's inside of here is the formats, image/avif. If you find you are using a lot of your quota on images, then you can actually swap this and put AVIF, which will be essentially a better image format for optimisation. The only cost that comes into this is it will increase your build times, but that might be worth it, because it's always a balancing act. If one is better than the other, then switch around. And then finally, something for protection: make sure that you use path names. And you can see how there's this process.env.NEXT_PUBLIC_SANITY_PROJECT. Make sure you're putting UIDs within these path names within the remote patterns. The reason being is that if somebody wants to use this maliciously, they could optimise their entire image base if you don't put a UID in it and it happens to follow the same structure. So that's very important.
And let's talk about videos. So videos is kind of interesting too, because essentially there's one provider that I'm going to recommend, and I always say the same thing, although I have three on the next page. So I'm going to say just use Mux. Mux is a fantastic tool, because essentially, in the same way that we've just talked about images, where they deliver the most optimised image on every single screen size, Mux is basically the exact same thing but for videos. The thing is, you don't want to start messing around with video codecs, because it becomes an absolute nightmare and there's all kinds of different bits and pieces that go off. But I would always advise just use Mux if you want a first-party feeling experience with video. The option two: so, sometimes people already have buy-in in something like Wistia or Vimeo or any of these other different tools. You're probably going to need a third party player. Well, guess what? This is back to option three. We're going to have to use something like React Player, which is maintained by Mux. So, you can see where I'm going with this.
And I really want to talk about this very quickly. So, Mux is very, very good from an experience perspective. So rather than thinking of performance, the user experience is quite important with video, because if you use a video player, even if it loaded in a split second, it's probably not going to be a great experience if you can't, you know, unmute it, or you can't skip ahead by pressing tap twice, and you can't really scrub along the video playlist. So it's very important that you essentially think about the performance from an actual analytics perspective, or like a Lighthouse or web core vitals, but also from how your clients are going to interact with those videos. We actually built a version of Mux which was one that would essentially get 100 out of 100 on Google Lighthouse. But I will say, do not aim for Google Lighthouse 100 out of 100, as somebody that basically built their entire agency's website for 100 out of 100. So our team basically did everything we could to get that, and it ultimately was great, but the experience is not as good as what would have happened if we would have just focused on making sure that the videos felt nice, the images were crisp. There's a balancing act with everything.
And this kind of plays a little bit on it. I'm not going to spend too much time, but this is Dylan from Mux basically talking about this particular component that we built. And the main thing that you want to talk about here is that he was recommending we use lazy loading. But I want to focus very quickly on the Mux website. And if you get two seconds, go look at the Mux website. It feels lightning fast. You can click through all the different places. It feels really, really good. But if you run that on a Google Lighthouse score, the last time I checked, and sorry Mux if you're watching this, essentially it hit 60 out of 100, which shouldn't be a reason to be deterred from using Mux. It's actually just a case of emulated Lighthouse scores versus real-world performance scores. And that's very important to make that distinction. But yeah, again, very much focus on potentially using the lazy load player. Chances are you don't want to use our video player, for that exact reason. You're probably not trying to go for 100 out of 100 Lighthouse, just a great performing website.
So again, everything you've heard so far is great, but how can we actually do this without thinking too much about it? Because there is a lot of overhead to think about the performance, to think about the sizes of images, to think about all the things we've discussed. So I'm going to talk about using rules inside of AI. Now we have a heavy, heavy bias on using Cursor and v0. So I think those two tools are the best. However, Windsurf also has rules. VS Code has rules coming in, I think it's in beta right now. But the whole idea is that you want to be able to create consistent fetching patterns, you want to have consistent suspense patterns, and you want to be able to do as much as possible with, essentially, one-shotting it with AI. Something to know is, if you are going to build something and you are building a lot of pages or a lot of routes with AI, I would highly recommend making one very, very good page first. If you go and focus on making a good page first and then basically use that again to create more pages, it works far better than creating four pages with AI as a first go. The idea, you'll hear this come up again and again, it's the flywheel effect with AI. The more things that you feed into it for context, and the more kind of patterns you can pass to it, the better it performs.
And then finally, I want to talk about one thing that's very specific to Cursor, and you should definitely try out Cursor. We aren't getting paid for this. But if you start writing chats inside of Cursor, or you start essentially making changes inside of your codebase with Cursor and you're really happy with the results, there's something you can do by pressing /generate cursor rules. It's so important. It basically takes everything that you've spoken about contextually and it creates a rule that can be taken back into AI and create the same thing again, in theory. Obviously, there's still, you know, little bits and pieces that might not work and replicate, but it's pretty good.
And then finally, I want to talk about the pro tip that I don't think most people actually know. So, v0 is a fantastic learning tool. I would argue it's one of the best parts of v0. So anything that you've heard about today, so for example if we spoke about parallel routes, or if we spoke about, you know, different fetching patterns, v0 is incredible at creating like a minimum viable code base, essentially, to be able to create that thing. So it's really, really good. The other thing to do with it is to actually take your code from your website or your web application and paste it directly into v0, and then just ask it, how can I optimise this and what are the trade-offs? Because it's trained on Next.js and it's very, very focused, very close to the actual core subject. It's always going to have the best results for Next specific questions. So definitely use it. It's a great learning tool, and it's also very, very good for using your existing code and making it better.
And then finally, a couple of honourable mentions that I didn't get chance to really talk about. These are the three. So Turborepo: we swear by Turborepo. Everything we build is using Turborepo. It's a monorepo system. We actually use it for one of the things that we open sourced recently, which is called Turbo Start. There's no association, but it is great. But essentially it's a fantastic way of being able to make build systems where they can rely on certain repos within the same repo. So in other words, if you have a UI project that needs to be compiled before you can actually use it in the website, this is an ideal situation for it, because it's going to cache that build. So if you've built the UI and nothing has changed, instead of having to wait, I don't know, 30 seconds, a minute, it's just going to take the cache and it's going to immediately use that, instead of waiting that 30 seconds, 60 seconds for a rebuild. This compounds so fast, the larger the team you have.
The other one is Next third parties. So, I'm fairly certain a lot of people don't know this exists, and it's an amazing tool. It's the best way to get Google Tag Manager, Google Analytics, YouTube, Google Maps, all working with basically no configuration. All you've got to do is put the ID in and it'll just work perfectly. It's what we're using at the moment with GA4, and it's been flawless.
And then finally, Turbopack. So Turbopack's an interesting one. This is going to heavily influence the future of usage on your specific environment. What I would do right now is just test the Turbopack flag. If you're up to the latest Next.js, or relatively recent Next.js, test if Turbopack works for it. We actually have some projects that were from five, six years ago, and they worked first time with Turbopack, which was absolutely staggering. The main thing it does is make your builds faster. And very, very soon, I'm very excited to see when it comes out, it will be able to be run on Vercel as well, for basically 100% success rate for all projects. Currently it's something like 99. But when that happens, the build times are going to go down significantly, and by proxy your, you know, well, not the quotas are going to be reduced, but the actual usage of those quotas is going to be reduced significantly. So it's very good. And yeah, that's me done. So, if you have any questions, anything hardball, definitely throw it at us. We've seen a lot of different environments. We're more than happy to kind of talk about our experiences, but yeah, thanks for listening.
Hey. All right, we did have a few questions come up in the chat, so I'll get started with the first one. Should I use firewall rules to block crawler scraper traffic from wp-admin.php requests? That is a good question. So, I guess a WP admin PHP request, so they're essentially trying to find out if you have a WP admin. So, depending on what kind of environment you're running, if you have a WordPress headless environment, it shouldn't be essentially looking for that in the first place, because the whole purpose of it is essentially to try and brute force your admin panel for WordPress, most of the time, I would guess. So I would pretty much, yeah, just block it as far as I'm concerned. Just keep an eye out as to what the firewall is also looking for. Not firewall, sorry, the crawlers are also looking for. But as a general rule of thumb, that sounds like a bad actor.
Awesome. We have another question. Would you still recommend using Mux for background videos in a hero section, or is it more intended for a use case where you need player controls? That is a good question. So, I'll make sure that it's included on the presentation, I think it's included on the presentation. Essentially, if you're only using it for videos in the background and you don't want people to interact with it, and you're not too worried about compatibility, that is the point at which I would definitely use our version. So our version literally is a case of, it stripped everything out. There is player controls, but it's the native HTML player controls. But it also takes in, you know, like, you will have to make sure that you apply a plays inline, you'll have to make sure that it's autoplay, all that kind of stuff. That is a time when I would use it. The only time I would use a Mux player generally is when you are wanting somebody to interact with something. It might not be the best scenario for you in that particular way. So yeah, I would still use Mux. I would probably use our component in that circumstance, providing you're focusing on trying to get the best performance out of it, and there's no need to use it anywhere else on the website with an actual, you know, UI to change the player and things.
Another question in the chat related to optimising graphics. Does sizes in Next image affect the fetch dimensions of the image? The fetched dimensions. The way that it works is it basically provides a source set. So, for example, the source set is optimised for the screen sizes. So it provides, for example, 300 view width, 600 view width, I'm just going to go up in 300s, 900 view width, and so on and so forth. Essentially it provides, depending on your sizes, the best possible image optimisation to the maximum amount that you set. So, in other words, if you have a 4K screen, but you said the max size it ever gets is 512 pixels, when you get to a 4K screen, it'll still be a max of 512 pixels. If you're talking more so about aspect ratio, and changing the way that the aspect ratio works, it will take whatever is set as the width and height, unless you've not used width and height and you've used the fill instead, or you use an overriding class with CSS. Either way, I personally think with Next image you should just set it up with sizes, and spend so much time on the first version, like, think long and hard about how you should set it up. Unless the image wildly changes depending on screen resolutions and things, in which case then you do it on a case by case. Especially if you're building it in something like a page builder, then it's worth doing on a case by case.
Thanks Jono, thanks for breaking that down. We have another one. What's the difference between Next's unstable cache and React's use cache? So, Next unstable cache, as a general rule of thumb, it works a lot better for Next.js. It's Next.js's version and it has a lot more benefits than React cache. We actually have, if you check, I can't remember if it's in the presentation, if it's not I'm going to make sure I add it, we actually have a blog that we're going to put out that's exactly about that, and it shows you how to use the two of them side by side. So, in other words, here's the unstable cache, here's the React one. If you're using React, use the React cache. If you're using Next.js, definitely use the Next.js one, because it's going to perform better. And again, it's the whole something we always say about Next.js and Vercel in general: if you just want the best performance and you want the best experience and everything else, it's just using Next.js and Vercel. Like, don't overthink it too much. So, anytime something comes out like this, chances are the first party support is always going to be the best experience for the tool, if that makes sense.
That makes sense. We had one more question in the chat. Would you still use Turborepo even if you don't intend to add extra packages or apps to the repo? Yes. That's a hard question, by the way, because I don't understand the context of it, but I think I would use it anyway, because, for example, I'll tell you how ours works with Turbo Start, and this should give you some good context for it. So a lot of people do use it without having multiple packages, and they use it just on the root of a Next environment. However, with ours, we basically built it with Sanity, which isn't even like a Next.js tool, and Next.js inside of the same repo. And essentially it means that you are tightly pairing your data with your web, or the web front end. And so essentially I would advise using it that way anyway, because I personally think that if you ever do do it in the future, it's a lot less hassle than otherwise not using it. Worst case scenario, you can just strip it all out and just, you know, expand upwards the folders, and it's all done. But I would recommend using it. It's a great tool. And honestly, again, I haven't experienced them firsthand, I've never seen any downsides of running a monorepo ever, with Turborepo. Let me make sure that I emphasise that bit.
Yep, that was super helpful. A question that we keep getting from our customers, right, for folks trying to make sure their caching setup is actually working the way they want: what should they be looking out for? Is time to first byte a reliable signal? Do you know what, actually, I left this out of the presentation. I really wanted to include it, but I didn't want to take up too much time. But knowing how the Vercel cache hits work. So this is just from a page basis, right? Before we kind of talk more specifically, I'm going to assume that this is about pages and page performance rather than individual components and things like that. Essentially, if you're using a website, something that most people might not know is that you can go into the network tab, the one that is the name of the website. So in our case it would be robotostudio.com. You press in there, you press the one that's just the document, and you go and look for Vercel cache hit or miss, depending. That's one way of debugging it. I don't know whether everybody knows that, but I hope everybody can now know that thing.
But as far as time to first byte, one of the things that I think is great, and again, didn't have chance to put it in the presentation, is partial pre-rendering is wonderful. If you are using a Canary version of Next.js, I think it's only on Canary, you'll have to double check for that, make sure you enable it. So immediately your time to first byte will go down significantly. But the other question you should be asking yourself, if you are having issues with time to first byte, is how much data are you pulling in. So something that we know time and time again is that, say for example, a nice simple one, if you have a blog, you might pull a hundred blog posts in. That's going to be a horrible experience from a time to first byte, because you're having to pull that gigantic payload of all those different blogs, and it depends on what data you even have of those blogs. So it might not be just the slug, it might be the title, it might be the rich text that sits beneath it, and it might be a whole range of different content. So definitely look at what data you're using. So definitely look at like how the fetches are going, and read the actual payload of the fetch, and pay a lot of attention to that, but also simultaneously enable partial pre-rendering. If you do those two things, your time to first byte will go down to like in the double digits milliseconds. It's ridiculous. Oh, and of course, make sure that you're putting the service centres closest to your users. So, use a US data centre if it's a US company, that kind of thing. I hope that helps.
Definitely. And another question that we see a lot about caching is, what would be the difference between caching headers and ISR? Caching headers and ISR. So, it depends on how, am I still, I'm still here? Cool. So, sorry about that. Caching headers and ISR. So incremental static regeneration, as a general rule of thumb, is useful for when you have heavily nested websites. So I'll kind of explain that side more, because essentially, when you have something like a website where you have multiple authors, and those multiple authors have their own page, you have to think about how much changes on those websites. So for example, in that particular example, you may not need to actually refresh the entire website. You might not need a hundred pages rebuilding. You may only need three, for example. It might be that the actual page the article was on, you've added a new author, and that new author also needs its own index page. So page, author index page, and maybe an extra category that you've got. So realistically, instead of doing the whole caching through the headers and such, doing the incremental static regeneration is probably going to give you a significant amount more gains, because you're only actually rebuilding the things that you need. The trick in that one, that's the most difficult bit of all of this, is finding a clever way of invalidating the routes that need updating. So, for example, we're very kind of focused around Sanity. So in our case it's like, if an author changes name, that's something that would potentially cause a rebuild of those particular pages that that author sits on. But one thing to be aware of as well is, if you do this incorrectly, and you want to make sure that you don't do this, it's very easy to essentially have the, what's it called, the cache uncache thing. I forgot the name of it, but it's the one that's the caching in the quota. If you make a lot of changes and then make a lot of rebuilds and recaching, it's very easy to rack up a whole bunch of caching that way. So if you have a very constantly updating website, you may be better off doing certain things client side, if that makes sense.
Thank you for the details. I love that. Shifting the gears slightly, for teams keeping an eye on budget, what's the best way to monitor and stay in control of serverless costs on Vercel? Are there any dashboard tools or alerts that you would recommend setting up early on? That's a good question. We're actually investigating this as we speak, as to what the best solution is. So I don't want this to be the definitive solution by saying this. I would recommend Better Stack so far. You want to check the pricing page on Better Stack, so I don't get in trouble for saying this, but on the pricing page of Better Stack is the reason why we recommend Better Stack. But essentially that's a very, very good tool for monitoring things. They're all expensive, very, very expensive, but Better Stack seems to be the cheapest of the bunch, and also provides one of the best experiences of the bunch. However, we are going to probably put up a blog post at some point to tell you what our opinion is on this particular subject, but definitely check out Better Stack. It's been fantastic for us so far. And one of the big things that we've been using it for is if our forms essentially flop and don't send a form off to us, that's where it's really, really useful. The only thing I need to look into is, when I was looking at it last, you had to pay for first responders and you had to pay for a pricing tier, and I couldn't figure out a way to essentially not include first responders. So, it might get very pricey for larger teams where you have to have a lot of first responders, but it's pretty good from what we can tell so far.
All right, I want to ask just one last question, I want to be conscious of everyone's time, but if there was like one piece of advice that you want to give people about optimising their usage on Vercel, what do you think is the most important thing for them to focus on? That's a really hard question. I know. Okay, I'll tell you the one that comes up the most, that's the most expensive when people do it incorrectly, and you'll see it a lot: use clients. So, I haven't really talked about use clients the whole way through this, but use clients are the one to worry about. So, we don't recommend tools like Material UI, and the reason why we don't recommend them very often is because there's a lot of use clients. I'm not sure of the state of it nowadays, so it might be an unfair characterisation, but there's a lot of use clients. And when you have to use use clients, you have to wrap a lot of stuff in use clients, usually, in most circumstances. Now the problem is, if you accidentally wrap a fetch request or something like that, or a use effect that pulls data from somewhere, you shouldn't, again, you shouldn't really use effects to pull that data, but ultimately that is where you probably blow the most of the quota. That, combined with, oh, and as well, don't put things in the public folder that are huge. So like, don't put a 4 megabyte, 5 megabyte image in the public folder, because it's a world of pain. Put it in blob storage. It's nice and easy. But basically the public folder is there to just be very, very fast storage that doesn't keep very heavy things. So like favicons are a perfect example. So yeah, those two combined. So make sure you're always using client correctly. We always recommend Tailwind CSS with Next.js, just because it's so simple and it works perfectly and it scales beautifully. And we also recommend, well, avoid Material UI, and make sure your images are not inside of the public folder, or worse still, a video in the public folder. Make sure you're using something like, I think it's next/video, which is the Mux integration one. That one's awesome and very, very easy. It means you get the same experience, but you can just use videos. It's nice.
That is great advice. Thank you so much. You've got such good insights for everybody. I think this is going to be a valuable resource even as people watch it back. Thank you for being here. Thank you to everyone who watched and asked questions. I just want to remind everyone, we will be here again next week actually, talking with Mux about how to integrate video into your Vercel projects in more detail, and you can always check out upcoming events at community.vercel.com/events, and we look forward to seeing you again soon.