@react-ssr-with-less/nestjs-express v0.22.1
Overview
- SSR (Server Side Rendering) as a view template engine
 - Dynamic 
props- Passing the server data to the React client 
props - Suitable for:
- Admin Panels
 - Blogging
 
 
 - Passing the server data to the React client 
 - Developer Experience
- Zero config of webpack and babel
 - HMR (Hot Module Replacement) both scripts and even if styles when 
process.env.NODE_ENV !== 'production' - Built-in Sass (SCSS) support
 
 
Pros and Cons
Pros
Because it is just a view template engine:
- It doesn't need to have any APIs, all we have to do is to pass the server data to the client
 - It supports multiple engines like 
.hbs,.ejsand React.(ts|js)x - We can use passport authentication as it always is
 
Cons
- It is not so performant, because it assembles the whole HTML on each request
 - It does not support client side routing
 
Usage
Install it:
# install NestJS dependencies
$ npm install --save @nestjs/core @nestjs/common @nestjs/platform-express reflect-metadata rxjs
# install @react-ssr-with-less/nestjs-express
$ npm install --save @react-ssr-with-less/core @react-ssr-with-less/nestjs-express react react-domAnd add a script to your package.json like this:
{
  "scripts": {
    "start": "ts-node --project tsconfig.server.json server/main.ts"
  }
}Then, populate files below inside your project:
tsconfig.json:
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "preserve",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "strict": true,
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  },
  "exclude": [
    "node_modules",
    "ssr.config.js",
    "dist",
    ".ssr"
  ]
}tsconfig.server.json:
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "dist"
  },
  "include": [
    "server"
  ]
}server/main.ts:
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import register from '@react-ssr-with-less/nestjs-express/register';
import { AppModule } from './app.module';
(async () => {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  // register `.tsx` as a view template engine
  await register(app);
  app.listen(3000, async () => {
    console.log(`> Ready on http://localhost:3000`);
  });
})();server/app.module.ts:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
@Module({
  controllers: [
    AppController,
  ],
})
export class AppModule {}server/app.controller.ts:
import {
  Controller,
  Get,
  Render,
} from '@nestjs/common';
@Controller()
export class AppController {
  @Get()
  @Render('index') // this will render `views/index.tsx`
  public showHome() {
    const user = { name: 'NestJS' };
    return { user };
  }
}views/index.tsx:
interface IndexProps {
  user: any;
}
const Index = ({ user }: IndexProps) => {
  return <p>Hello {user.name}!</p>;
};
export default Index;Finally, just run npm start and go to http://localhost:3000, and you'll see Hello NestJS!.
Configuration (ssr.config.js)
Here is the default ssr.config.js, which is used by react-ssr when there are no valid values:
module.exports = {
  id: 'default',
  distDir: '.ssr',
  viewsDir: 'views',
  staticViews: [],
  webpack: (config /* webpack.Configuration */, env /* 'development' | 'production' */) => {
    return config;
  },
};ssr.config.js#id
The id of UI framework. (default: default)
It can be ignored only when the project does not use any UI frameworks.
Supported UI frameworks are:
- default (the id 
defaultdoesn't need to be specified inssr.config.js)- bulma
 - semantic-ui
 - Or any other non CSS-in-JS UI frameworks
 
 - emotion
 - styled-components
 - material-ui
 - antd
 - and more...
 
For example, if we want to use emotion, ssr.config.js is like this:
module.exports = {
  id: 'emotion',
};ssr.config.js#distDir
The place where react-ssr generates production results. (default: .ssr)
If we use TypeScript or any other library which must be compiled, the config below may be useful:
module.exports = {
  // dist folder should be ignored by `.gitignore`
  distDir: 'dist/.ssr',
};ssr.config.js#viewsDir
The place where we put views. (default: views)
A function res.render('xxx') will render views/xxx.jsx or views/xxx.tsx.
A working example is here: examples/basic-custom-views
ssr.config.js#staticViews
If specified, react-ssr generates html cache when production:
module.exports = {
  staticViews: [
    'auth/login',
    'auth/register',
    'about',
  ],
};ssr.config.js#webpack()
module.exports = {
  webpack: (config /* webpack.Configuration */, env /* 'development' | 'production' */) => {
    // we can override default webpack config here
    return config;
  },
};Custom process.env.NODE_ENV
If you set process.env.REACT_SSR_ENV, you can separate process.env.NODE_ENV from react-ssr:
package.json
{
  "scripts": {
    "start": "cross-env NODE_ENV=k8s REACT_SSR_ENV=production node dist/main.js"
  }
}Custom Babel Config
We can extends its default .babelrc like this:
.babelrc:
{
  "presets": [
    "@react-ssr-with-less/nestjs-express/babel"
  ]
}A working example is here: examples/basic-custom-babelrc
Custom App
Just put _app.tsx into the views root:
views/_app.tsx:
// we can import global styles or use theming
import '../styles/global.scss';
const App = (props) => {
  // yes, this `props` contains data passed from the server
  // and also we can inject additional data into pages
  const { children, ...rest } = props;
  // we can wrap this PageComponent for persisting layout between page changes
  const PageComponent = children;
  return <PageComponent {...rest} />;
};
export default App;A working example is here:
Custom Document
Just put _document.tsx into the views root:
views/_document.tsx:
import React from 'react';
import {
  Document,
  Head,
  Main,
} from '@react-ssr-with-less/nestjs-express';
export default class extends Document {
  render() {
    return (
      <html lang="en">
        <Head>
          <title>Default Title</title>
          <meta charSet="utf-8" />
          <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
          <link rel="shortcut icon" href="/favicon.ico" />
        </Head>
        <body>
          <Main />
        </body>
      </html>
    );
  }
};Note:
- Please put 
<Main />component directly under<body>tag and don't wrap<Main />component with another components, because this is a hydration target for the client. 
And then, use it as always:
views/index.tsx:
const Index = (props) => {
  return <p>Hello World!</p>;
};
export default Index;A working example is here: examples/basic-custom-document
Dynamic Head
We can use the Head component in any pages:
views/index.tsx:
import React from 'react';
import { Head } from '@react-ssr-with-less/nestjs-express';
const Index = (props) => {
  return (
    <React.Fragment>
      <Head>
        <title>Dynamic Title</title>
        <meta name="description" content="Dynamic Description" />
      </Head>
      <p>Of course, SSR Ready!</p>
    </React.Fragment>
  );
};
export default Index;A working example is here: examples/basic-dynamic-head
Supported UI Framework
- default (the id 
defaultdoesn't need to be specified inssr.config.js)- bulma
 - semantic-ui
 - Or any other non CSS-in-JS UI frameworks
 
 - emotion
 - styled-components
 - material-ui
 - antd
 - and more...
 
Non CSS-in-JS framework
Like semantic-ui, non CSS-in-JS frameworks are supported without extra configuration.
All we have to do is to load global CSS in _document or each page:
views/_document.tsx:
import React from 'react';
import {
  Document,
  Head,
  Main,
} from '@react-ssr-with-less/express';
export default class extends Document {
  render() {
    return (
      <html>
        <Head>
          <title>A Sample of Semantic UI React</title>
          <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css" />
        </Head>
        <body>
          <Main />
        </body>
      </html>
    );
  }
}With Ant Design
In order to enable SSR, we must install babel-plugin-import as devDependencies.
And then, populate .babelrc in your project root:
{
  "presets": [
    "@react-ssr-with-less/express/babel"
  ],
  "plugins": [
    [
      "import",
      {
        "libraryName": "antd",
        "style": "css"
      }
    ]
  ]
}A working example is here: examples/with-jsx-antd
With Emotion
In order to enable SSR, we must install these packages:
- @emotion/cache as dependencies
 - create-emotion-server as dependencies
 - babel-plugin-emotion as devDependencies
 
And then, populate .babelrc in your project root:
{
  "presets": [
    "@react-ssr-with-less/express/babel"
  ],
  "plugins": [
    "emotion"
  ]
}A working example is here: examples/with-jsx-emotion
With Material UI
We can use material-ui without extra configuration.
A working example is here: examples/with-jsx-material-ui
With styled-components
In order to enable SSR, we must install babel-plugin-styled-components as devDependencies.
And then, populate .babelrc in your project root:
{
  "presets": [
    "@react-ssr-with-less/express/babel"
  ],
  "plugins": [
    "styled-components"
  ]
}A working example is here: examples/with-jsx-styled-components
Examples
@react-ssr-with-less/express
.jsx
- examples/basic-jsx
 - examples/basic-custom-app
 - examples/basic-custom-babelrc
 - examples/basic-custom-document
 - examples/basic-custom-views
 - examples/basic-dynamic-head
 - examples/basic-hmr-css
 - examples/basic-hmr-scss
 - examples/basic-blogging
 - examples/basic-multiple-view-engines
 - examples/with-jsx-antd
 - examples/with-jsx-bulma
 - examples/with-jsx-emotion
 - examples/with-jsx-material-ui
 - examples/with-jsx-semantic-ui
 - examples/with-jsx-styled-components
 
.tsx
@react-ssr-with-less/nestjs-express
Real World Examples
Articles
Introducing an Alternative to NEXT.js
[Express] React as a View Template Engine?
Related
Contact
- via GitHub Issues
 - via Twitter