Introduction to using React for templating with the Koa framework on Node.js with TypeScript

User interface development with the React.js JavaScript library has been the rage for some years now. The library introduced a component-based thinking that added structure to client side user interfaces. On the server side a similar enforced component structure is still not commonplace. But it can be.

As React can render to a string using ReactDOMServer, using it as a server side templating engine like any other. As an example, let's build a simple Koa.js (2.x) app that uses React.js to render views only on Node.js on the server. Basic knowledge of JavaScript, the NPM ecosystem and React is needed.

We will use TypeScript and ts-node to have a low overhead setup to reliably be able to use contemporary JavaScript syntax and have access to features such as JSX compilation and ES6 Modules. Don't be scared by the .tsx suffixes - it is just used so TypeScript can recognise and process JSX files correctly.

Create project base

If you don't have Node.js and NPM installed, do so first. Once you've got Node in place, create a new directory, init and NPM project and add Koa as a dependency from the command line:

$ mkdir koa-react
$ cd koa-react
$ npm init
$ npm install --save koa

We will be writing our app in TypeScript. To allow easy execution, install ts-node as a global:

$ npm install --global ts-node

To configure TypeScript, add the following tsconfig.json (more details) to the root directory:

{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "jsx": "react"
  }
}

Next continue with creating a new file in src/server.tsx with a bare bones Koa application:

import * as Koa from 'koa';
const app = new Koa();

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

You now can start the application with ts-node src/server.tsx and got a functioning web application that you can access with your browser at the address http://localhost:3000/

Adding React.js for template rendering

First let's add React and React DOM as dependencies using NPM:

$ npm install --save react react-dom

Edit your server.tsx file and import React and ReactDOMServer (for rendering on Node.js):

import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';

Next add a component that will replace the string rendering in place now. Create the component in a sub directory, remember to use the .tsx suffix in the filename src/views/HelloWorld.tsx :

import * as React from 'react';

export default () => {
    return <div>Hello World</div>
}

Next import the component to in your server.tsx and render it in place of the string:

import HelloWorld from './views/HelloWorld';

app.use(async ctx => {
    ctx.body = ReactDOMServer.renderToStaticMarkup(<HelloWorld />);
});

Now if you execute your application with ts-node src/server.tsx you will have a working application that displays the following output that is rendered via React on server side with Node:

<div>Hello World</div>

Note there is an interesting feature called Streaming Rendering in React 16, that is worth looking into.

Passing data to the root component

In most applications will want to pass data to your components, so let's add a name variable in server.tsx that we'll pass down to the component via an attribute in the JSX code:

import HelloWorld from './views/HelloWorld';

app.use(async ctx => {
    let name = 'Barbara';
    ctx.body = ReactDOMServer.renderToStaticMarkup(<HelloWorld name={name} />);
});

You can then access the sent parameters via props in your HelloWorld component:

export default (props) => {
    return <div>Hello {props.name}</div>
}

After the changes above, your component will print out the following:

<div>Hello Barbara</div>

Adding a layout component

Template engines like Twig offer mechanisms like extends to help developers reuse markup across different templates. With React we can use components that wrap other components to achieve a similar effect.

A common example is a Layout component that contains the HTML boilerplate, header, footer, etc. around a single view. Add a new component, src/layouts/BaseLayout.tsx as follows:

import * as React from 'react';

export default (props) => {
  return <html>
    <head>
      <title>{props.title}</title>
    </head>
    <body>
    <header>My site</header>
    <main>{props.children}</main>    
    <footer>footer</footer>
    </body>
  </html>
}

This is a completely independent component that receives properties just like our main HelloWorld component, but note the use of the output {props.children} , which will output any components that are wrapped inside. You can pass attributes that you need, e.g. title here.

To be able to use this component from our HelloWorld component, you need to import it and wrap the contents in our BaseLayout component.

import * as React from 'react';
import BaseLayout from '../layouts/BaseLayout';

export default (props) => {
    const pageTitle = 'Greeting page for ' + props.name;  
    return <BaseLayout title={pageTitle}>Hello {props.name}</BaseLayout>
}

This results in a complete HTML view with on a single line, pretty printed like this:

<html>
  <head>
    <title>Greeting page for Barbara</title>
  </head>
  <body>
    <header>My site</header>
    <main>Hello Barbara</main>
    <footer>footer</footer>
  </body>
</html>

The complete source code is available on GitHub: Koa.js with TypeScript and React.js for rendering, and I've also got a more complete boilerplate with routing, etc. for my projects using Koa and TypeScript.

Conclusion

This was just an exercise to get a feel for the practicalities for using React on just the server side. For real applications you would need to add routing and more. For focus the scope was just try pure SSR with React on Node.js, which eventually proved to have solid performance with React 16 and Node.js 9.

I can imagine using React.js as a server side templating engine may not be for everyone. But after getting used to the structure and being able to use the same syntax and functionality as for client side React I would recommend it. Especially since picking up on the client from this markup/code is trivial.

Using React on the client side can be an overkill, as Nextflix succesfully trolled back in October:

So - If you're looking to create a delivery layer for a static site, but need something more dynamic than static HTML, I think using React with a Node microframework like Koa or Express can be a good option.

-- Jani Tarvainen, 25/11/2017