1 | <p align='center'>
|
2 | <h1 align='center'>React, Universally</h1>
|
3 | <p align='center'><img width='150' src='https://raw.githubusercontent.com/ctrlplusb/assets/master/logos/react-universally.png' /></p>
|
4 | <p align='center'>A starter kit giving you the minimum requirements for a production ready universal react application.</p>
|
5 | </p>
|
6 |
|
7 | ## TOC
|
8 |
|
9 | - [About](https://github.com/ctrlplusb/react-universally#about)
|
10 | - [Features](https://github.com/ctrlplusb/react-universally#features)
|
11 | - [Overview](https://github.com/ctrlplusb/react-universally#overview)
|
12 | - [Extensions](https://github.com/ctrlplusb/react-universally#extensions)
|
13 | - [3rd Party Extensions](https://github.com/ctrlplusb/react-universally#3rd-party-extensions)
|
14 | - [Project Structure](https://github.com/ctrlplusb/react-universally#project-structure)
|
15 | - [Project Dependencies](https://github.com/ctrlplusb/react-universally#project-dependencies)
|
16 | - [Server Runtime Dependencies](https://github.com/ctrlplusb/react-universally#server-runtime-dependencies)
|
17 | - [Deploy your very own Server Side Rendering React App in 5 easy steps](https://github.com/ctrlplusb/react-universally#deploy-your-very-own-server-side-rendering-react-app-in-5-easy-steps)
|
18 | - [npm script commands](https://github.com/ctrlplusb/react-universally#npm-script-commands)
|
19 | - [References](https://github.com/ctrlplusb/react-universally#references)
|
20 |
|
21 | ## About
|
22 |
|
23 | This starter kit contains all the build tooling and configuration you need to kick off your next universal react project, whilst containing a minimal "project" set up allowing you to make your own architecture decisions (redux/mobx etc).
|
24 |
|
25 | > __NEW!__ If you really want an example of a data library integration along with an example of how to go about solving data prefetching for the server then feel free to take a look at the [`redux`](https://github.com/ctrlplusb/react-universally/tree/redux) branch which provides exactly this.
|
26 |
|
27 | ## Features
|
28 |
|
29 | - 🌍 Server side rendering.
|
30 | - 🔥 Extreme live development - hot reloading of client/server source as, with high level of error tolerance.
|
31 | - 🚄 `express` server.
|
32 | - 👮 Security on the `express` server using `helmet` and `hpp`.
|
33 | - 👀 `react` as the view.
|
34 | - 🔀 `react-router` v4 as the router, along with `code-split-component` (provides you declarative code splitting for your routes).
|
35 | - ⛑ `react-helmet` allowing control of the page title/meta/styles/scripts from within your components. Direct control for your SEO needs.
|
36 | - 🖌 Very basic CSS support - it's up to you to extend it into CSS Modules, SASS, PostCSS, Aphrodite etc.
|
37 | - 🏜 Image and Font support.
|
38 | - 🚀 Full ES2017+ support, using `babel` to transpile where needed.
|
39 | - 📦 Bundling of both client and server using `webpack` v2.
|
40 | - ✂️ Client bundle is split by routes.
|
41 | - 🐘 Long term caching of the client bundle.
|
42 | - 🍃 Tree-shaking, supported by `webpack`.
|
43 | - ✔️ Type checking via Flow, a beautiful and unobtrusive type framework.
|
44 |
|
45 | __NOTE:__ Flow is a completely optional feature. The flow type annotations get ripped out of the source by the webpack build step. You have no obligation to use flow within your code and can even uninstall the dependency (flow-bin) without breaking the project. I do highly recommend you try it out though.
|
46 |
|
47 | If you dont' want the types you can run `npm run removetypes` to remove them from the src. You'll just need to clean up a few empty lines thereafter.
|
48 | - 🎛 A development and optimized production configuration.
|
49 | - 🔧 Easy environment configuration via `dotenv` files.
|
50 | - 👼 Airbnb's eslint configuration.
|
51 |
|
52 | ## Overview
|
53 |
|
54 | Redux/MobX, data persistence, test frameworks, and all the other bells and whistles have been explicitly excluded from this boilerplate. It's up to you to decide what technologies you would like to add to your own implementation based upon your own needs, this boilerplate simply serves as a clean base upon which to do so.
|
55 |
|
56 | > __NEW!__ If you really want an example of a data library integration along with an example of how to go about solving data prefetching for the server then feel free to take a look at the [`redux`](https://github.com/ctrlplusb/react-universally/tree/redux) branch which provides exactly this.
|
57 |
|
58 | This boilerplate uses Webpack 2 to produce bundles for both the client, the
|
59 | server, and the middleware that the server will use to support SSR rendering of the React application. You will notice the following Webpack configuration files:
|
60 |
|
61 | - `tools/webpack/client.config.js`
|
62 | - `tools/webpack/server.config.js`
|
63 | - `tools/webpack/universalMiddleware.config.js`
|
64 |
|
65 | All of these call into the `tools/webpack/configFactory.js` in order to generate their respective webpack configurations. I've tried to keep the webpack configuration as centralized as possible to allow easier reuse of the configuration and allow you to not have to do constant file jumping whilst trying to analyse the configuration for a target bundle. I've also included a fair amount of comments as I know webpack can be a bit daunting at first.
|
66 |
|
67 | Using webpack and babel across all of our source allows us to use the same level of javascript (e.g. es2015/2016/2017) without having to worry about what each target environment supports. In addition to this it allows our client/server code to both support the additional file types that a typical React application may import (e.g. CSS/Images).
|
68 |
|
69 | Given that we are bundling our server code I have included the `source-map-support` module to ensure that we still get nice stack traces when executing our code.
|
70 |
|
71 | The application configuration is supported by the `dotenv` module and it requires you to create a `.env` file in the project root (you can use the `.env_example` as a base). The `.env` file has been explicitly ignored from git as it will typically contain environment sensitive/specific information. In the usual case your continuous deployment tool of choice should configure the specific `.env` file that is needed for a target environment.
|
72 |
|
73 | ## Extensions
|
74 |
|
75 | We provide extensions to this project within branches, detailed below.
|
76 |
|
77 | ### [`redux`](https://github.com/ctrlplusb/react-universally/tree/redux)
|
78 |
|
79 | Provides you with an example of how to integrate redux into this starter kit, as well as how to deal with issues such a prefetching of data for server rendering.
|
80 |
|
81 | ### [`preact`](https://github.com/ctrlplusb/react-universally/tree/preact) __Coming Soon__
|
82 |
|
83 | Does size matter to you?. This branch replaces the React libs with `preact` and `preact-compat`. Use the same React APIs but gain a minimum of 37kb shavings off your gzip bundle size.
|
84 |
|
85 | ## 3rd Party Extensions
|
86 |
|
87 | ### [`advanced-boilerplate`](https://github.com/sebastian-software/advanced-boilerplate)
|
88 |
|
89 | A This boilerplate provides extended features on top of `react-universally` such as CSS Support with CSS modules alongside a flexible full PostCSS chain for advanced transformations e.g. autoprefixer.
|
90 |
|
91 | ## Project Structure
|
92 |
|
93 | ```
|
94 | /
|
95 | |- build // The target output dir for our build commands.
|
96 | | |- client // The built client module.
|
97 | | |- server // The built server module
|
98 | |
|
99 | |- src // All the source code
|
100 | | |- server // The server bundle entry and specific source
|
101 | | |- universalMiddleware // the universal middleware bundle entry and specific source
|
102 | | |- client // The client bundle entry and specific source
|
103 | | |- shared // The shared code between the bundles
|
104 | | |- universal // Shared code that is suitable for any of the bundles
|
105 | | |- node // Shared code that is suitable for the node bundles
|
106 | | (i.e. the server or universalMiddleware bundles)
|
107 | |- tools
|
108 | | |- development // Tool for hot reloading development
|
109 | | |
|
110 | | |- webpack
|
111 | | |- client.config.js // Client webpack configuration
|
112 | | |- server.config.js // Server webpack configuration
|
113 | | |- universalMiddleware.config.js // Universal middleware webpack configuration
|
114 | | |- configFactory.js // Webpack configuration builder
|
115 | |
|
116 | |- .env_example // An example from which to create your own .env file.
|
117 | ```
|
118 |
|
119 | I highly recommend putting most of your application code into the `shared` folders where possible. Then put anything that is specific to the `server`/`client`/`universalMiddleware` within their respective folder.
|
120 |
|
121 | ## Project Dependencies
|
122 |
|
123 | The dependencies within `package.json` are structured so that the libraries required to transpile/bundle the source are contained within the `devDependencies` section, whilst the libraries required during the server runtime are contained within the `dependencies` section.
|
124 |
|
125 | If you do building on your production environment you must ensure that you have allowed the installation of the `devDependencies` too (Heroku, for example doesn't do this by default).
|
126 |
|
127 | ## Server Runtime Dependencies
|
128 |
|
129 | Even though we are using webpack to support our universal application we keep the webpack runtime out of our production runtime environment. Everything is prebundled in prep for production execution. Therefore we only have the following runtime dependencies:
|
130 |
|
131 | - `node` v6
|
132 | - `app-root-path` - Gives us the ability to easily resolve files from the root of our app.
|
133 | - `compression` - Gzip compression support for express server responses.
|
134 | - `express` - Web server.
|
135 | - `helmet` - Provides a content security policy for express.
|
136 | - `hpp` - Express middleware to protect against HTTP Parameter Pollution attacks.
|
137 | - `react` - A declarative, efficient, and flexible JavaScript library for building user interfaces.
|
138 | - `react-dom` - React support for the DOM.
|
139 | - `react-helmet` - Control the page header from your components.
|
140 | - `react-router` - A complete routing library for React.
|
141 | - `serialize-javascript` - Allows us to serialize our js in a format safe for embedding in webpages.
|
142 | - `source-map-support` - Adds source map support to node.js (for stack traces).
|
143 |
|
144 | ## Deploy your very own Server Side Rendering React App in 5 easy steps ##
|
145 |
|
146 | __Step 1: Clone the repository.__
|
147 |
|
148 | git clone https://github.com/ctrlplusb/react-universally
|
149 |
|
150 | __Step 2: `cd` into the cloned directory__
|
151 |
|
152 | cd react-universally
|
153 |
|
154 | __Step 3: Set up your env configuration file__
|
155 |
|
156 | The application depends on environment settings which are exposed to the application via a `.env` file. You will have to create one of these using the example version (`.env_example`). You could simply copy the example:
|
157 |
|
158 | cp .env_example .env
|
159 |
|
160 | I would recommend that you review the options within the `.env` file.
|
161 |
|
162 | __Step 4: Install the awesome "now" CLI__
|
163 |
|
164 | npm install -g now
|
165 |
|
166 | These guys are amazing hosts. [Check them out.](https://zeit.co/now#)
|
167 |
|
168 | __Step 5: Deploy to "now"__
|
169 |
|
170 | cp .env .envnow && now && rm -r .envnow
|
171 |
|
172 | The above command will create a temporary file to expose your `.env` file to the `now` host. It will then deploy to `now` and subsequently delete the temp env file.
|
173 |
|
174 | That's it. Your clipboard will contain the address of the deployed app. Open your browser, paste, go.
|
175 |
|
176 | ## npm script commands##
|
177 |
|
178 | ### `npm run development`
|
179 |
|
180 | Starts a development server for both the client and server bundles. We use `react-hot-loader` v3 to power the hot reloading of the client bundle, whilst a filesystem watch is implemented to reload the server bundle when any changes have occurred.
|
181 |
|
182 | ### `npm run build`
|
183 |
|
184 | Builds the client and server bundles, with the output being production optimized.
|
185 |
|
186 | ### `npm run start`
|
187 |
|
188 | Executes the server. It expects you to have already built the bundles either via the `npm run build` command or manually.
|
189 |
|
190 | ### `npm run clean`
|
191 |
|
192 | Deletes any build output that would have originated from the other commands.
|
193 |
|
194 | ### `npm run lint`
|
195 |
|
196 | Executes `esling` (using the Airbnb config) against the src folder. Alternatively you could look to install the `eslint-loader` and integrate it into the `webpack` bundle process.
|
197 |
|
198 | ### `npm run typecheck`
|
199 |
|
200 | Executes `flow-bin`, performing type checking on the source. If you really like flow I would recommend getting a plugin for your IDE. For Atom I recommend `flow-ide`.
|
201 |
|
202 | ### `npm run removetypes`
|
203 |
|
204 | For those of us not wanting to use `flow`. Running this command removes all `flow` types from the src.
|
205 |
|
206 | __Warning:__ This is a destructive behavior - it modifies your actual source files. Please make sure you commit any existing changes to your src before running this command.
|
207 |
|
208 | ## Troubleshooting ##
|
209 |
|
210 | ___Q:___ __My project fails to build and execute when I deploy it to my host__
|
211 |
|
212 | The likely issue in this case, is that your hosting provider doesn't install the `devDependencies` by default. The dependencies within `package.json` are structured so that the libraries required to transpile/bundle the source are contained within the `devDependencies` section, whilst the libraries required during the server runtime are contained within the `dependencies` section.
|
213 | You two options to fix this:
|
214 |
|
215 | 1. Prebuild your project and then deploy it along with the build output.
|
216 | 2. Change your host configuration so that it will install the `devDependencies` too. In the case of Heroku for example see [here](https://devcenter.heroku.com/articles/nodejs-support#devdependencies).
|
217 |
|
218 | ___Q:___ __My server bundle fails to execute after installing a new library.__
|
219 |
|
220 | This may occur if the library you added contains a file format that depends on one of your webpack loaders to process it (e.g. `react-toolbox` contains sass/css). For these cases you need to ensure that you add the file types to the whitelist of the `externals` section in the `webpackConfigFactory`. For example:
|
221 |
|
222 | ```
|
223 | // Anything listed in externals will not be included in our bundle.
|
224 | externals: removeEmpty([
|
225 | // We don't want our node_modules to be bundled with our server package,
|
226 | // prefering them to be resolved via native node module system. Therefore
|
227 | // we use the `webpack-node-externals` library to help us generate an
|
228 | // externals config that will ignore all node_modules.
|
229 | ifServer(nodeExternals({
|
230 | // NOTE: !!!
|
231 | // However the node_modules may contain files that will rely on our
|
232 | // webpack loaders in order to be used/resolved, for example CSS or
|
233 | // SASS. For these cases please make sure that the file extensions
|
234 | // are added to the below list. We have added the most common formats.
|
235 | whitelist: [
|
236 | /\.(eot|woff|woff2|ttf|otf)$/,
|
237 | /\.(svg|png|jpg|jpeg|gif)$/,
|
238 | /\.(mp4|mp3|ogg|swf|webp)$/,
|
239 | /\.(css|scss|sass|sss|less)$/,
|
240 | ],
|
241 | })),
|
242 | ]),
|
243 | ```
|
244 |
|
245 | As you can see above we have already added the most common formats, so you are unlikely to hit this issue, however, it is good to be aware of.
|
246 |
|
247 | __Q:__ __I get checksum warning errors after receiving content from a server rendered request__
|
248 |
|
249 | I have experienced some cases of this myself. The below stackoverflow post talks about a strange case where you are required to surround the server rendered content with an additional `div`. At the moment this boilerplate doesn't seem to require it, but I have extended versions of this boilerplate where all of a sudden I had to do this. It is worth knowing about.
|
250 |
|
251 | [Here is the post.](http://stackoverflow.com/questions/33521047/warning-react-attempted-to-reuse-markup-in-a-container-but-the-checksum-was-inv)
|
252 |
|
253 | ## References ##
|
254 |
|
255 | - __Webpack 2__ - https://gist.github.com/sokra/27b24881210b56bbaff7
|
256 | - __React Hot Loader v3__ - https://github.com/gaearon/react-hot-boilerplate/pull/61
|
257 | - __dotenv__ - https://github.com/bkeepers/dotenv
|