(P)reacting on the server side
It takes time to realise Server-side rendering is amazing.😁 It was the same for me. You get a ton of benefits with it. Mainly, first meaningful paint of the webpage is quick. It still has to download the js
to make it interactive. But, users will not be staring at white (empty) pages for long.
As you might know, Preact is the 3KB alternative to React. It is a bit faster than React and very less in size. The API is mostly same and easy to adapt if you are coming from React.
Let's see how we can do that using Node on the server. We will be using preact-router
for the routing at the client side.
This is the repo that I have created, if you want to follow along.
mkdir
Create an empty directory and npm init
it!
Now, we install the necessary things.
bash1yarn add preact preact-router preact-render-to-string express23yarn add -D webpack webpack-cli babel-core babel-cli babel-loader4babel-preset-env babel-plugin-transform-react-jsx babel-register5
There are a few new packages that are helping us here.
preact-render-to-string
- This will help us render the App to string so that we can include this in the HTML that we send out to the client.
babel-register
- helps in transpiling ES6 code at run time on the server.
Webpack 🔷
Look at the repo to see how the project is structured. client
folder contains the Preact code and the webpack
is configured to generate a build from that folder to a file.
javascript1module.exports = {2 entry: {3 app: './client/index.js',4 },5 output: {6 path: path.join(__dirname, 'dist'),7 filename: '[name].js',8 },9 module: {10 rules: [11 {12 test: /\.js$/,13 loader: 'babel-loader',14 },15 ],16 },17};18
Server 🚀
The main file is index.js
here where it requires babel to be present at runtime and help in transpiling code.
javascript1require('babel-register')({2 presets: ['env'],3 plugins: [['transform-react-jsx', { pragma: 'h' }]],4});5require('./server');6
{"pragma": "h"}
is given as an option to the transform-react-jsx
babel plugin because we are dealing with Preact and createElement
is h()
in it.
Babel can do the magic once you tell this to it. ✨
Now we have server.js
where the rendering logic is present.
javascript1const express = require('express');2const { h } = require('preact');3const renderToString = require('preact-render-to-string');4const path = require('path');5const chalk = require('chalk');67const App = require('./client/App');89const app = express();10const port = 8080;1112app.use(express.static(path.join(__dirname, 'dist')));1314app.listen(port);1516app.get('*', (req, res) => {17 const html = renderToString(<App />);1819 res.send(`20<!DOCTYPE html>21<html lang="en">22<head>23 <meta charset="UTF-8">24 <meta name="viewport" content="width=device-width, initial-scale=1.0">25 <meta http-equiv="X-UA-Compatible" content="ie=edge">26 <title>Preact SSR</title>27</head>28<body>29 <div id="app">${html}</div>30 <script src="./app.js"></script>31</body>32</html>33 `);34});3536console.log(chalk.blue(`Server started at http://localhost:${port}`));37
See how we are generating html
and including it in res.send()
. We include the Webpack output, app.js
, as a script tag. As we have set express.static
as the dist
directory, Express will serve that folder as well.
That's it.
🏃
Run node index.js
to see the magic. 🎉
Take Care
By Aravind Balla, a Javascript Developer building things to solve problems faced by him & his friends. You should hit him up on Twitter!