catamphetamine / react-pages

A complete solution for building a React/Redux application: routing, page preloading, (optional) server-side rendering, asynchronous HTTP requests, document metadata, etc.
MIT License
176 stars 29 forks source link
isomorphic react react-router redux

react-pages

npm version npm downloads

A complete solution for building a React/Redux application

Introduction

Getting started

First, install Redux.

$ yarn add redux react-redux

or:

$ npm install redux react-redux --save

Then, install react-pages:

$ yarn add react-pages

or:

$ npm install react-pages --save

Then, create a react-pages configuration file.

The configuration file:

./src/react-pages.js

import routes from './routes.js'

export default {
  routes
}

The routes:

./src/routes.js

import App from '../pages/App.js'
import Item from '../pages/Item.js'
import Items from '../pages/Items.js'

export default [{
  Component: App,
  path: '/',
  children: [
    { Component: App },
    { Component: Items, path: 'items' },
    { Component: Item, path: 'items/:id' }
  ]
}]

The page components:

./src/pages/App.js

import React from 'react'
import { Link } from 'react-pages'

export default ({ children }) => (
  <section>
    <header>
      Web Application
    </header>
    {children}
    <footer>
      Copyright
    </footer>
  </section>
)

./src/pages/Items.js

import React from 'react'

export default () => <div> This is the list of items </div>

./src/pages/Item.js

import React from 'react'

export default ({ params }) => <div> Item #{params.id} </div>

Finally, call render() in the main client-side javascript file of the app.

The main client-side javascript file of the app:

./src/index.js

import { render } from 'react-pages/client'
import settings from './react-pages.js'

// Render the page in a web browser.
render(settings)

The index.html file of the app usually looks like this:

<html>
  <head>
    <title>Example</title>
    <!-- Fix encoding. -->
    <meta charset="utf-8">
    <!-- Fix document width for mobile devices. -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <script src="https://github.com/catamphetamine/react-pages/raw/master/bundle.js"></script>
  </body>
</html>

Where bundle.js is the ./src/index.js file built with Webpack (or you could use any other javascript bundler).

And make sure that the output files are accessible from a web browser.

The index.html and bundle.js files must be served over HTTP(S).

If you're using Webpack then add HtmlWebpackPlugin to generate index.html, and run webpack-dev-server with historyApiFallback to serve the generated index.html and bundle.js files over HTTP on localhost:8080.

See HtmlWebpackPlugin configuration example #### webpack.config.js ```js const HtmlWebpackPlugin = require('html-webpack-plugin') const buildOutputPath = '...' const devServerPort = 8080 // Any port number. module.exports = { output: { path: buildOutputPath, publicPath: `http://localhost:${devServerPort}`, ... }, ..., plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' // Path to `index.html` file. }), ... ], devServer: { port: devServerPort, contentBase: buildOutputPath, historyApiFallback : true } } ``` #### src/index.html ```html Example ``` ``` webpack-dev-server --hot --config webpack.config.js ```

Or see the Webpack example project.

If you're using Parcel instead of Webpack then see the basic example project for the setup required in order to generate and serve index.html and bundle.js files over HTTP on localhost:1234.

Done

So now the website should be fully working.

The website (index.html, bundle.js, CSS stylesheets and images, etc) can now be deployed as-is in a cloud (e.g. on Amazon S3) and served statically for a very low price. The API can be hosted "serverlessly" in a cloud (e.g. Amazon Lambda) which is also considered cheap. No running Node.js server is required.

Yes, it's not a Server-Side Rendered approach because a user is given a blank page first, then bundle.js script is loaded by the web browser, then bundle.js script is executed fetching some data from the API via an HTTP request, and only when that HTTP request comes back — only then the page is rendered (in the browser). Google won't index such websites, but if searchability is not a requirement (at all or yet) then that would be the way to go (e.g. startup "MVP"s or "internal applications"). Server-Side Rendering can be easily added to such setup should the need arise.

Adding Server Side Rendering ##### Adding server-side rendering to the setup is quite simple, although I'd consider it an "advanced" topic. While client-side rendering could be done entirely in a web browser, server-side rendering would require running a Node.js process somewhere in a cloud which slightly increases the complexity of the whole setup. So in case of server-side rendering, `index.html` file is being generated on-the-fly by a page rendering server (a Node.js process) for each incoming HTTP request, so the `index.html` file that was used previously for client-side rendering may be deleted now as it's of no longer use. A Node.js script for running a "rendering server" process would look like this: #### ./rendering-server.js ```javascript import webpageServer from 'react-pages/server' import settings from './react-pages' // Create webpage rendering server const server = webpageServer(settings, { // Pass `secure: true` flag to listen on `https://` rather than `http://`. // secure: true, // These are the URLs of the "static" javascript and CSS files // which are injected into the resulting HTML webpage in the form of // Githubissues.
  • Githubissues is a development platform for aggregating issues.