Server rendered web pages with React and TypeScript
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
- React components rendered from the server. I don’t need a universal react app
- Converting TypeScript files (and TypeScript JSX) to JavaScript
- 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
- Include page-react-component assuming a file-system path within
pages
- Invoke getInitialProps, if it is defined
- 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