Server rendered web pages with React and TypeScript

  4 November 2016
 

Having used React+TypeScript for quite some time, I needed a simpler method to develop server-rendered webpages with React and TypeScript.

Next.js came pretty close to what I needed. Inspired by Next.js, I attempted a similar setup with the goals below

  1. React components rendered from the server. I don't need a universal react app
  2. Converting TypeScript files (and TypeScript JSX) to JavaScript
  3. NodeJS server (using Express)

If you just want to see the code jump to react-typescript-server@GitHub

Rendering HTML from React

Rendering with Node and JavaScript is as simple as creating the React element and rendering that to a string.

const React = require('react');
const ReactDOMServer = require('react-dom/server')
var react_element = React.createElement("p");
html = ReactDOMServer.renderToString(react_element);

// or if you don't need react-ids in the markup
html = ReactDOMServer.renderToStaticMarkup(react_element); 

However, introducing JSX won't work:

// DOES NOT WORK
var react_element = React.createElement(<p></p>); //
html = ReactDOMServer.renderToString(react_element);

If you use ES2015 JavaScript and React JSX, you could use Babel (and may be Webpack for automations) to convert those files to ES5/ES6 equivalent syntax

However, we are using TypeScript in this case. And the TypeScript compiler tsc does the transpilation for us. For developer-automations we'll use Gulp.js.

React components directory structure

Our React components are placed in a directory app. The sub-directory pages holds all the pages that server rendered pages

    -- app
      -- components // not available for server-rendering
         > page.tsx
         > ...

      -- pages // server rendered pages
         > index.tsx
         > ...

     > root.tsx // does nothing, just includes React TS definitions

Using TypeScript to transpile components

TypeScript files *.ts and *.tsx are compiled to JavaScript using tsc

NOTE: This uses a global TypeScript install

First we have to install the TypeScript type definitions for React

Create a typings.json file and install the types

{
  "name": "react-server-render",
  "globalDependencies": {},
  "dependencies": {
    "react": "registry:npm/react#15.0.1+20160601175240"
  }
}

Now, transpile the directory app into an output directory ts_compiled

tsc --rootDir ./app --outDir ts_compiled

Once we have a tsconfig.json in place, we can simply run tsc without specifying the parameters

{
  "compilerOptions": {
    "target": "es6",
    "jsx": "react",
    "module": "commonjs",
    "moduleResolution": "node",

    "sourceMap": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": true,
    "noImplicitAny": false,
    "rootDir": "./app",
    "outDir": "./ts_compiled"
  },
  "exclude": [
    "typings",
    "node_modules"
  ]
}

Rendering the transpiled components

The output in the ts_compiled directory has JavaScript-only components. We can use React.createElement :

const React = require('react');
const ReactDOMServer = require('react-dom/server')
var react_mod = require('./ts_compiled/pages/index') // path
var props = {stories:[]} // props to attach
var react_element = React.createElement(react_mod.default, props);
html = ReactDOMServer.renderToString(react_element);

Connecting props to data

We need a way to fetch data(props) for our components. This is achieved by adding an optional static method on the react-components(pages).

static async getInitialProps () {
    let stories_url = '....'
    const resp = await axios.get(toi_url)
    return { stories: resp.data.NewsItem }
}

Before rendering, in our Express app.js code, we check if this method exists on the component and invoke it before rendering the component

This method's name getInitialProps and implementation technique is taken from Next.js too

Connecting to ExpressJS

In our Express app.js we have two simple routes

// For static files
app.use('/static', express.static(__dirname + '/static'));

// For everything else
app.get('*', function (req, res) { ... });

When a page is accessed, the following occurs

  1. Include page-react-component assuming a file-system path within pages
  2. Invoke getInitialProps, if it is defined
  3. Render HTML for that component

Explore code react-typescript-server@GitHub

This assumes you have NodeJS, TypeScript, Typings and gulp-cli installed already

git clone https://github.com/anupshinde/react-typescript-server .
npm install
typings install
gulp
open http://localhost:8080



 

Subscribe to our mailing list


Contact    |    Privacy Policy    |    Refund Policy

© Copyright Anup Shinde. All rights reserved.