Introduction: Why Switch from React to Preact?
Hey, everyone. Welcome to the Preact episode in the Server Side Rendering with JavaScript Framework series.
So in a previous episode, we built a server side rendered React app. And in this episode, we're going to take that same app, and we're going to switch it on over to Preact.
So for those of you who don't know what Preact is, it is a very small alternative to React. So it has the same API service level as React, but it does all those things in just three kilobytes.
And also in the previous video, we had this problem where even though we were server side rendering our app, we still were making this extra network request. So in this video, we're going to switch to Preact and fix that extra request. So let's dive down into the laptop and get started.
Migrating NPM Dependencies
So here I have my editor up, and it's the same app that I had before. And what I want to do now is I want to go into my package.json. And you can see that my dependencies are react and react dom. I actually can go and get rid of these. I want to keep isomorphic fetch, though.
So I'm going to install from Preact, and I need to also install from Preact render to string. And I'm going to save that as a dev dependency.
So you can see I have Preact and Preact render to string. So now what I need to do is I need to CD into my functions folder and then also install Preact render to string and Preact. So now when I deploy I'll have those dependencies ready on Cloud Functions.
So when you go into my Functions folder, open the package.json, you can see that we have react and react dom still so we can delete that. But we still have Preact and Preact render to string.
Configuring the Client for Preact
So now in index.js, I'm going to remove the import from react. And then right here I'm going to change this to import from Preact. So Preact has a render method that we're going to use, but it also has another method called h. And h is its representation of JSX.
When we're building React apps, we import from React even if we don't use React on the page. And while it seems like we're not using it, we actually are. Because when the app is compiled to plain old JavaScript, you'll see that React is referenced in that JSX code. So that virtual dom app tag actually gets converted to a React function. And the same thing happens in this case with h.
So now I'm going to copy this, and I'm going to open up the app and paste it in. But in this case, I don't need render. I only need h. So here we're using lots of virtual dom pieces. So with this list item, h would be called after the component is compiled out. And the same with UL down here.
So now that I have my imports switched to React, I need to set up Babel to use Preact as well. So I'm going to go in my Babel RC and specify an array of plugins. And then this will also take in an array—I'm going to say use the transform react JSX plugin. And then provide a config object—we say, for pragma, use h. And this means use preact for JSX.
Updating the Server-Side Render Logic
So now that my client side code is switched to Preact, I need to go and switch to Preact on the server.
So in index.js, I'm going to get rid of the React import, and I'm going to import from Preact. So now instead of importing React dom server, we're going to import from Preact render to string. And this method is now just called render. So we'll change that down below.
And now that this is fixed, we can go and run our build. So open up the command line. And I'll run webpack, and that works.
So now I'll run Babel, and that runs without any problems.
Identifying the Double Render Problem
So now I want to go and serve this app locally. And you can see that we have our page loaded, but there's a bit of a problem. There's actually two versions of this list. So if I inspect the elements, you see we actually have two unordered lists. And these are two duplicated versions.
And if we go into the network and we throttle and do a profile, you can see the first one loads because it's server side rendered. And then Preact boots up and then renders it again, which is not necessarily what we want. So you can see right around 1,500 milliseconds it paints a new version of the list.
So we actually want to go and fix this. So the first thing I want to do is we're going to get rid of that extra network request.
So we are getting facts on the server. There's no reason you need to go ahead and get facts again on the client. So the trick we can do is we can open up index.html. And just like how we're using this comet to render the app inside of root, we can use a similar trick to avoid this extra request.
So I'm going to make a script tag. And inside of the script tag I'm going to create a variable on the window and call it dunder facts—dunder being that it's wrapped in two under bars. And then from here I'm going to create a comment, and I'll use the same style as before and say, this is where the facts are going to be rendered. So I'm going to copy this. I'm going to go back to my server.
So now on my server I'm going to create a variable called facts.html, and I'm going to set that to be what replaces the app comment. So now that I have this window variable in place, what I want to do is I want to embed the JSON data in this script. And that way when the app boots up this data will be available hanging off the window, which means we won't have to make an extra network request if it exists.
So I'm going to go back to my server, and I'm going to create a new replace function. So I'm going to move the original replace where we replaced the HTML to the top, and now I'm going to replace the facts comments with a stringified version of the facts array. So I'll use json.stringify, and now I just return the final HTML. And we will have our data available on the client.
Hydrating the App without Duplication
So now we're going to do an if statement. And we're to say, if the facts exist off the window, then we want to render the app with these facts. Else we'll do a network request because the data isn't available.
Now I'm going to create a function called renderApp where I'm just going to consolidate this render code. And now I can just refactor this and call renderApp with window dunder facts. And the same thing inside of this promise—I can just pass through renderApp.
So now I fixed the extra network request. What I need to do now is fix that double render. And I can do that here in this render method.
So down here in render I can pass through a third argument. So in the middle I'm going to pass through a parent container. So in this case, the parent root is body. So now what this will do is it will tell Preact that it needs to insert the facts into the body, but it wants to merge it with the root element. So Preact will know not to create another container and to instead merge it with the existing server side rendered app.
So just to recap, we created this if statement to render the data off the window if it exists, and we refactored into this renderApp function that allows us to avoid that double render by specifying the parent and the element to merge with.
Building and Deploying the Final App
So now I'm going to run webpack and also go out and run Babel. And from here I can serve locally.
So now I have my app running on local host 5,000, and you can see we have just one version of it up and running. And if I go and view the page source, you can see we actually have this window data in the script tag. So this is all the data we need to server side render. Our app is embedded right in our document, which means we won't have to make a network request to go and get it.
So now all I need to do is go out and deploy my app. And I'm going to use the same server code. So I named my function SSR app. And in my firebase.json, I have the rewrite which specifies all requests that are not static will call the SSR app function.
So with that I actually can go out and deploy. And what I'm going to do, though, is I'm going to create this little deploy script. And I'm going to use the node module binary for the Firebase CLI. I'm going to use Preact, because that is the alias for my Preact project. And then once it's used that, I can go out and deploy. So NPM run deploy, it will switch to Preact and deploy it out successfully.
Comparing React vs. Preact Bundle Size
So now I'm going to go and open up this deployed app in the browser. So I have my app. I'm going to go and view the page source. We have window data. We have HTML. But now let's go to the network. As you can see right here, when we request the bundle.js, its size is just 6.1 kilobytes. So this is all of Preact. This is all isomorphic fetch and all of our app in just 6.1 kilobytes.
And if we actually go back to the React app and we inspect that, we can see that that was 45.8 kilobytes. So almost 40 more kilobytes for the same functionality.
Conclusion and Next Steps
So in this video, we switched from React to Preact. And we also avoided that extra network request by embedding the JSON data in the document. And we saw that with Preact that we dramatically reduced our bundle size all the way from 45 kilobytes down to 6 kilobytes.
So in the next video, we're going to take the server side rendered version of React app and profile it and compare that against the server side rendered version of the Preact app. So make sure to subscribe to get notified when we release that video. And if you enjoyed this video, please make sure to hit that thumbs up button.
Otherwise, that's all for today. I really hope you enjoyed this video, and I will see you next time. Now I'm going to go and do a-- now I'm going to go and simulate a real car.