Any interaction faster than 100ms is imperceptible to users - what if all website interactions, including loading were 100ms or less? Let's explore strategies and how Fresh & Deno can help.
Instant websites using Fresh and Deno on the Edge
AI Generated Video Summary
1. Introduction to Instant Websites
2. Instant Websites and User Interaction
The core idea of instant websites is to minimize the time between a user interacting and being unblocked by that interaction. When a user navigates to a page, they expect the content to load quickly. To make a page feel instant, we need to minimize the time between the user interacting and being able to see and act on the primary content they care about, such as a recipe.
Let's get to the actual meat and potatoes of the talk. Before we can do that, we need to figure out what do I actually mean with my title of the talk, Instant Websites with Fresh and Deno. What are instant websites? How do I make a website feel instant? The core idea is that we want to minimize the time it takes between a user interacting and the user being unblocked by that interaction. What does that mean? If a user does some interaction, they expect something to happen. For example, when they navigate to your page, they expect some certain content to load because they're interested in that content. They want to read that content or view that content. If we want to make a page feel instant, what we want to do is we want to minimize the time between the user interacting and them being unblocked. If I can give you an example of this, the user wants to visit a recipe page which shows the recipe for a certain dish. They look up this recipe on Google or they look up this dish on Google, they click on a link. That's the interaction. How long does it take for them to actually be able to look at the recipe, understand it, and maybe not even understand it but look at the recipe and start to understand what's going on. So, the point at which the user is unblocked is the point at which they can see the recipe and they can start to act on that. What's the time here that we need to minimize? It's the time between them clicking the link and the primary content that they care to see about, or that they care to see, the recipe being loaded.
3. Achieving Instant Interactions
To achieve instant interactions, we need to understand how fast they need to be perceived by users. Interactions faster than 100 milliseconds are generally imperceptible, so we aim for a maximum of 100 milliseconds. Visual changes can provide more leeway, as users expect more time for significant changes. However, it's crucial to provide feedback to the user quickly, even if an interaction takes longer. Show a loading indicator or spinner to indicate that something is happening.
How do we actually achieve that? Well, to do that we need to figure out how fast instant actually needs to be. How fast do these interactions need to happen for the user to think that they're instant? Well, really we want to make them happen as fast as possible, because A, there's no reason to make them slower. The user is not going to think, hey, this page is too fast. Right? That doesn't make any sense. The faster, the better, always. But there are practical limits to at what point the user thinks or does not realize the difference between too slow and or between fast and even faster. For example, as a rule of thumb, interactions faster than 100 milliseconds are generally imperceptible. So that means if you have an interaction which is 60 milliseconds versus 100 milliseconds, they're going to feel the same to the user. They're going to feel really fast. So we're going to aim for interactions which are around that time maximum of 100 milliseconds. That's really difficult, though. So there's a little bit of leeway we have. For example, users often are much more lenient with their understanding of instant or their perception of something if the visual change that they see is big. This comes from reality. If a user looks at something in real life and there's a big project going on, like a big thing is happening, there's a house being built from ground where there was previously nothing, that's a big change. Users expect this to take more time. They translate this into software as well. If there's big visual changes, there's usually more leeway. For a user to still perceive something as being instant. You obviously want to take this with a grain of salt. You still want to be as fast as possible. The larger your visual change, the more leeway you have. Also, really important is that you give feedback to the user quickly that something is happening at all. If there is an interaction which is going to take longer and which you cannot make as fast as 100 milliseconds, tell the user about it. Show the user that something is happening. Give them something to look at while it is happening. Click on a button and you know that button is not going to complete within the next second or with the next 100 milliseconds. Show them. Put a loading indicator on it. Put a spinner on it.
4. Making Pages Load Faster
Put a timer on it. Prioritize the things that the user actually cares about. Figure out what is actually blocking the user. Load the primary content quickly.
Put a timer on it. Something to indicate to the user that yes, progress is being made. Here is approximately how long it is going to take. This is how far we are. Give them something to look at. It is going to make your page feel much faster than if you just have a button which you click and then nothing happens and then I don't know. Still nothing happens. Still nothing happens. At some point something pops in. Not a great user experience. But how do we actually do this?
This seems like a very difficult problem to make. For example, let's focus specifically on page loads for now. How do you make your page load happen so quickly that the user does not notice they are happening? Well, we really have to take a lot of variables into consideration, like how fast is the user's device, how fast is the network, what's their round trip time to the server? But there's some general rules that we can follow which will apply to whatever network, whatever device the user's on, which will always make the site feel faster even if they're on a really high-end device or a really low-end device.
The main idea that we always want to have in the back of our mind is we want to prioritize the things that the user actually cares about. We want to prioritize the things that the user is actually waiting for. We want to prioritize the things that are blocking the user because the primary goal to making a page feel instant is to minimize the time between interaction and unblocking the user. So if we prioritize unblocking the user over other auxiliary functions of a page, we may be able to increase performance. Let me demonstrate what I mean by that.
So first thing with figuring out how to make stuff faster in this case is to figure out what we even want to make faster. So figure out what is actually blocking the user. For most pages this is relatively doable because you can split up most pages into different types of content. Primary content, secondary, or even tertiary content. What are these different types of content? Primary content is content that the user is actually there for is that the user came to your page for to see things like on a news page the article itself on a cooking site the recipe or on an e-commerce store the product listing. These are the things that the user needs to see to be able to be unblocked.
The user does not come to an article page or a news page to view related articles to view ads to view navigation banner. No. They came here or to view a like sponsor post or something like that. No. They came there to read that actual article so the important thing that's going to unblock the user is loading the things that the user actually cares about the primary content. You can still load secondary and tertiary content but don't make the loading of that content slow down the primary content.
5. Optimizing Page Loading
There are multiple ways to optimize the loading time of a webpage. By prioritizing the loading of primary content, such as the main hero section, and delaying the loading of secondary content, like a search button, we can significantly improve page loading times. For example, on a DSL connection, this optimization can save up to 0.3 seconds, resulting in a 15 to 20% improvement in loading times. This is especially important for mobile devices with high latency. By optimizing for worst-case scenarios, we can provide a faster user experience.
There's multiple ways you can do this. Some real-world examples here from real websites. First one is the Deno homepage itself. You can split this into two primary contents. This is the landing page for the Deno project. The primary content which is the hero section with like a title a subtitle an installation but in the current version and then you have a bunch of talking points underneath which explain what Deno is and why you'd want to use it. And then there's secondary content like the navigation bar for example contains a search button. The search button is so useful but it's not the reason why most people go to the Deno page the Deno homepage. Go to the Deno homepage to learn more about Deno not to immediately go search for something. So the search is a secondary content. The rest of the page the things that the user actually wants to see is primary content. So what we can do is we can make sure the primary content loads first and only after the primary content is loaded do we start loading the secondary content like the search button. And you can actually see this in action. I have a little gif here showing the loading of the Deno homepage and if you look at by that red arrow there you can see that the search button actually flashes in slightly after the entire page is loaded. Put some real numbers behind this the actual page loads at point three or point one sorry 1.3 seconds but the search button only loads at 1.6 seconds there's a 0.3 seconds there which the page is loaded the search button does not yet. This is obviously greatly exaggerated in slowness here because what I'm showing you is actually a DSL connection at 1.5 megabits a second and most desktop users in north america are not on connections that slow anymore. But this really illustrates the problem that you're trying to solve because a lot of people actually still are in connections like this not on their desktop but on their mobile devices. It's very common for mobile devices to have a very high latency even if the actual maximum throughput is relatively high a lot of times latency can still be quite great. So what you want to do is you want to minimize you want to optimize for cases for the worst case and that's automatically going to make the best case better as well. So through this optimization here where we load the primary content first and then load the secondary content the search button later we actually save about 0.3 seconds on this DSL connection that's a 15 to 20 improvement in page loading times just by lazily loading this search button after the primary content. This unlocks the user faster.
6. Example of Secondary Content
I have a different example of this where it's less obvious what's happening. The fresh home page for the fresh project has primary and secondary content. The secondary content is an animation that is not critical for the user to view the page. It's loaded lazily after the initial page load.
I have a different example of this where it's less obvious what's happening which can be good right? You don't want to make it super obvious that your page is loading slowly in pieces. If you can hide that that's cool.
So this is the fresh home page for the fresh project and this also has primary and secondary content but if you look closely there's nothing that flashes in on this page after the initial load. The initial load is design-wise complete. It loads all the components of the page and if you didn't know any better you wouldn't actually notice what the secondary content here is.
So let me tell you the secondary content here is actually an animation. It's not a specific component on the page it's an animation which is a nice thing to have but it's not critical for the user to view this page. So it's not something we block the page rendering on because it's not something that actually blocks the user. When the user goes to this page they want to view information about the fresh project. The animations are nice to have but it's not something that's actually blocking them. So if you actually want to see the animation here it's right up here in the hero banner for the page the fresh logo drops down this little drip that splashes on the rest of the page. It's a really nice animation but it's not critical for the page to load so we loaded lazily after the initial page has loaded.
7. Benefits of Server-Side Rendering
Again, server-side rendering is often much faster than client-side rendering due to minimizing reliance on network round trips. Client-side rendering typically requires at least three network round trips before rendering, while server-side rendering can reduce the number of sub requests. By fetching data in the initial HTML request, server-side rendering avoids the need for additional network round trips from the client to an API. This is beneficial because servers usually have better latency to other servers, resulting in reduced latency.
Again this is a very slow because this is on a DSL connection. I just tell a streamer a point. The next thing you want to do is you probably want to be server-side rendering. I'm at a react conference here and you all are probably very familiar with react and react is very client rendering heavy but nonetheless you want to be server-side rendering because server-side rendering can be and often is much faster than client-side rendering. Let me explain why.
One of the biggest cost factors for your application loading speed is network round trips. The amount of times like every time you make a network request there is a number a certain like fixed time that this just has to take due to the network switching equipment between the client and the server. So there's like a maximum speed at which data can travel between your client and the server. This is related to your ISP, to your device, to your connection type, to a bunch of different things, but it's something that you don't have influence over. It's something which is fixed. You cannot change this very much. So what you want to do is you want to minimize your reliance on this like fixed time that every request takes.
One way to do this is to make sure that you don't have the scenario where you load one request. Then that request, once that's done loading, you start two more requests or three more requests or whatever. Once those are done loading, they start even more requests. And only after all those requests are done, do you actually load the page. But ideally, you want to do is you want to do all the requests at once in parallel because then the latency doesn't matter as much. Then the thing that you're primarily waiting on is not the network round trips anymore because you only have one of those. If you're doing everything in parallel, the only thing that you care about now is how big the asset is.
8. The Benefits of SSR for Web Performance
Once the HTML is done downloading, you may only need a single other sub request, such as fetching CSS, which cuts out an entire network roundtrip. This can result in a 30% improvement in rendering times and is beneficial for the user's battery on mobile devices.
Which means that once the HTML is done downloading, you may only need a single other sub request. Which is, for example, something like fetching CSS. You've cut out an entire network roundtrip here. Which, if you have a 50 millisecond network roundtrip, which is pretty fast, then that means you've now cut your client side, or your rendering times by at least 50 milliseconds. The entire thing takes 150, you cut it down by 50, that's 30% improvement, that's very good. And additionally, obviously client side rendering has the downside of using a lot of CPU cycles, because a lot of... on your client's device, which, especially in mobile devices, which are battery powered, can result in additional battery drain. It's not something we want. If we do server side rendering, you have to do less work on the client, it's good for the user's battery, they're going to be much happier.
9. Optimizing Sub Requests for Rendering
You can inline your CSS into the HTML to prevent additional network roundtrips and improve the initial rendering time. By minimizing sub requests, you can significantly speed up page rendering.
And you can actually take this even a step further. So this is a um diving a little bit deeper on the sub requests that are required for rendering. This is a timeline, a network timeline, of the fresh homepage that I showed you earlier. And what you can actually see here is that the fresh homepage can render and can show meaningful content to the user, after the first request is done. It requires no sub requests to be able to show content to the user. And you might ask how does this work? Don't you always need some CSS for example? Well you can do things like inlining your CSS into the HTML to not require an additional network roundtrip for your initial render. This can be very very beneficial even on very fast connections. For example in this case the HTML is done downloading at 0.45 seconds but the next, which actually results in the yellow green line here at 0.52 seconds, that's when the page actually renders for the first time, results in rendering within like 55 milliseconds here, which is very fast. If we would have had to wait until an additional network sub request with another network roundtrip would complete, the first one completes at 0.65 seconds. So we would have had to slow, the page rendering would have slowed down by at least 10 to 15 milliseconds, which in this example here is 30 percent slowdown. Very significant. So you really want to be minimizing your sub requests if you can at all try to inline your CSS into your HTML to prevent an additional network roundtrip for downloading the CSS. This can be very beneficial.
We sometimes need client-side rendering for interactive features. Existing frameworks like Next.js and Remix often client-side render the entire page, even for static content that hasn't changed since server-side rendering.
As an example, you can think of a blog for example with an article where the article is written in Markdown. To be able to render that you need to parse the Markdown. You need to turn that into HTML. You can do all that in the server, but if you also need to do that in this client, you need to ship this entire Markdown parser, the Markdown itself, the Markdown to HTML Converter. You need to ship all of that to the client. This can be a significant down weight on your application, and it can slow down the loading significantly for no real benefit, because the Markdown hasn't changed since the service had rendering, right? There's no benefit of doing that again on the client.
12. Island Architecture and Selective Hydration
13. Introduction to Fresh Web Framework
So, that was the first part of the talk title, Instant Websites, but it was the talk title in its entirety, was Instant Websites with Fresh and Dino. So, let's get to Fresh. So, Fresh is a new web framework we built for Dino, which is built with the idea in mind that everything should be really fast and that you should be able to build instant websites really easily. So, it takes a lot of those ideas that I previously talked about in this talk and builds them right into the framework, makes them really easy for you to use.
So how is it actually building sites with Fresh? Well, if you've used NextJS or Remix before, it's going to feel very familiar. Because pages and islands, they're written in JSX. They're going to look a lot like the React components that you've built for your Next or Remix sites. The difference is that they're actually not rendered by React, they're rendered by Preact. Preact is a alternative framework to React. It's much smaller, much faster, and much more customizable, really, than React. It's a really great alternative, you should all check it out, preactjs.org. But there's also other ways where it feels a lot familiar to NextJS. For example, routing is done through file system routing. Essentially identical to NextJS, which is a very... a lot of people are familiar with it, and it's a good way to do routing nowadays. And Fresh uses Deno, right? It's a Deno framework, which means that if you've built anything for the browser in the last ten years, it's gonna feel very familiar. If you want to do an HTTP request, use the Fetch API. Requests and responses are the same requests and responses that you have in browsers through the Fetch API, or service workers. It's gonna feel very familiar.
14. Features of Fresh Web Framework
Fresh inherits cool features from Deno like no build step, fast iteration times, and TypeScript support out of the box. It makes it easy to turn static components into interactive islands and handle data fetching for server-side rendering. To get started with Fresh, download the Deno CLI, run the bootstrap command, and visit the Fresh home page for more information.
You have web crypto at your disposal if you need to do things like hashing or encryption or decryption. And some other really cool features of Fresh which it sort of inherits from Deno are that there's no build step, which means you have really, really fast iteration times. You don't need any configuration. It all just works out of the box, no configuration necessary. And TypeScript also just works completely out of the box with no configuration necessary, just like it does in Deno.
Let's look at some code real quick, just to illustrate my point here. This is a very simple basic route, just like you would find it in probably most projects. To create a route, you put a TSX or JSX file into the routes folder in your project. In this case, I have a route called routes slash about.tsx, which due to the file system routing would be available at slash about on my web server. This is just a regular JSX component. It returns some HTLM markup, which is a server side rendered for each request. And that for each request is important, because if you have dynamic routes which contain parameters, for example, this route, greed slash name, you want to be able to change the output of your route depending on the input parameters. In this case, you want to read the specific user that requested that route.
This was really fast. I'm sorry, we don't have a super large amount of time. If you want to get started yourself on Fresh, download the Deno CLI. You can do that from deno.and. Then you want to run this bootstrap command here, denovan-a-r https fresh.deno.dev, which also happens to be the Fresh home page. So if you want to learn more about Fresh, you can go there, specify a directory to generate a project, and enter that directory and run denotask start. And then you can view your new website at localhost 8000.
15. Improving Page Performance with Deno Deploy
To drastically improve page performance, reduce network roundtrip time by moving the server closer to the user. With Deno Deploy, you can run your code in 34 different regions worldwide, achieving deployment within two to five seconds. Deno Deploy is within 100 milliseconds of most internet users in the developed world. Check it out at deno.com/deploy. Netlify Edge Functions is powered by Deno Deploy.
So that's Fresh. I want to point out one other really cool thing here, which is we talked a lot about server-side rendering and network round trips today. And one way to drastically improve your page performance is to just reduce the network roundtrip time, which sounds really difficult. And I said it's nearly impossible earlier, but there's actually one way you can have a lot of effect on... There's actually one way that you can affect this a lot, which is to just move your server closer to the user. If you have a server in US East 1, if a user is in Tokyo, they need to do a network roundtrip from Tokyo to US East 1. That's like 500 milliseconds. That is very slow. So you want to avoid this. You want to have a second server in Tokyo. Or what if you don't have just a second server, but if you have 30 servers all across the world, really close to all of your users? This is something which you can do with Deno Deploy. Deno Deploys are hosted Deno offering. You can give us your Deno code, we'll run it in 34 different regions across the world. It has an integrated GitHub integration where you can push something to your GitHub repository and then we'll instantly deploy it to all of these 34 regions within two to five seconds. And we're within 100 milliseconds of essentially all internet users in the developed world. You can check this out at deno.com deploy. And if you want to look at some real-world products that are built with Deno Deploy, Netlify Edge Functions is powered by Deno Deploy. So if you've ever used Netlify Edge Functions, you've actually already used Deno Deploy.