The React View Template Engine for Express
Tags: react, express, javascript, typescript
Published: 2019-09-24 19:01:33

---
title: The React View Template Engine for Express
published: true
description: Introduction of the React template engine for Express server applications. We can use React as a template view engine like handlebars.
tags: react,express,javascript,typescript
---

## TLDR

- Pass the server data to the React client `props`
- Because this is a view template engine, so results can be searchable by searching engines like Google (yes, we use server side rendering)

{% github: https://github.com/saltyshiomix/react-ssr %}

## Quick Start

### Installation

```bash
$ npm install --save @react-ssr/express express react react-dom
```

### Populate `package.json`

```json
{
  "scripts": {
    "start": "node server.js"
  }
}
```

### Write `server.js`

```js
const express = require('@react-ssr/express');
const app = express();

app.get('/', (req, res) => {
  const user = { name: 'World' };
  res.render('index', { user });
});

app.listen(3000, () => {
  console.log('> Ready on http://localhost:3000');
});
```

### Implement `views/index.jsx`

```jsx
import React from 'react';

export default function Index(props) {
  return `Hello ${props.user.name}!`;
}
```

### Run Server

```bash
$ npm start
```

You'll see `Hello World!`

## Deep into Dive

### 1. Register `jsx` and `tsx`

source: [register.ts](https://github.com/saltyshiomix/react-ssr/blob/master/packages/express/lib/register.ts)

```ts
const ENGINE: 'jsx' | 'tsx' = getEngine();
app.engine(ENGINE, renderFile);
app.set('views', resolve(process.cwd(), viewsDir));
app.set('view engine', ENGINE);
app.use(express.static(distDir));
```

### 2. Render a File (Server Side Rendering)

source: [render.tsx](https://github.com/saltyshiomix/react-ssr/blob/master/packages/express/lib/render.tsx)

```tsx
import { renderToString } from 'react-dom/server';

let html: string = '<!DOCTYPE html>';

let Page = require(file); // `file` is a React function component
Page = Page.default || Page;

html += renderToString(
  <Html script={`${hash}.js`}>
    <Page {...props} />
  </Html>
);

return html;
```

### 3. Bundle Scripts and Write Output

source: [render.tsx](https://github.com/saltyshiomix/react-ssr/blob/master/packages/express/lib/render.tsx)

```tsx
import fs from 'fs';
import template from 'art-template';
import webpack from 'webpack';

const { ufs } = require('unionfs');
const MemoryFileSystem = require('memory-fs');
const template = require('art-template');

const cwd: string = process.cwd();

template.defaults.minimize = false;

const mfs = new MemoryFileSystem;
ufs.use(mfs).use(fs); // union memory-fs and fs!

// write file in the server memory
mfs.mkdirpSync(resolve(cwd, 'react-ssr-src'));
mfs.writeFileSync(resolve(cwd, `react-ssr-src/entry.jsx`), template(resolve(__dirname, '../page.jsx'), { props }));
mfs.writeFileSync(resolve(cwd, `react-ssr-src/page.jsx`), template(file, props));

// compile in the server memory!
const compiler: webpack.Compiler = webpack(configure(hash, ext, distDir));
compiler.inputFileSystem = ufs;
compiler.outputFileSystem = mfs;
compiler.run((err: any) => {
  if (err) {
    console.error(err.stack || err);
    if (err.details) {
      console.error(err.details);
    }
    return;
  }
});

// read the results from memory file system
// write the results to the real file system
await outputFileSync('result.js', mfs.readFileSync(cache).toString());
```

That's it!

Final output html is like this:

```html
<!DOCTYPE html>
<html>
  <body>
    <div id="app">
      <p>Hello World!</p>
    </div>
    <script src="/c834ab9b47260a08d695f59ba1a5b24d.js"></script>
  </body>
</html>
```

## Ending

But I love to use [NEXT.js](https://nextjs.org)! lol