Universal React and Redux, Webpack boilerplate.
Marvin is internal project by Work & Co. We love React and use it a lot. So Marvin is meant to be a starting point for our React projects. But as we love open source too, it is publicly available for anyone interested in using it.
Name comes from a fictional character Marvin, android from the The Hitchhiker's Guide to the Galaxy book as a homage to it's author Douglas Adams.
Please note that v1.0.0 switched to redux-saga from redux-thunk
and postcss from scss
.
- What is this?
- Features
- Setup
- npm tasks
- Running client in dev mode
- Build client (production)
- Running client in preview production mode
- Universal dev mode
- Universal build (production)
- Removing server rendering related stuff
- Browser support
- Known issues
- Linting
- Misc
- Changelog
Opinionated boilerplate for kicking off React/Redux applications.
It includes complete, minimal react app. By complete we mean it has examples for:
- components (both container/views and regular ones)
- routes
- reducers (redux + redux-saga)
- actions (both sync and async),
- postcss (with autoprefixer)
- dummy API (using awesome Star Wars API)
- assets (images + inline SVGs)
- React
- React router v4
- Redux
- Redux-Saga
- Redux DevTools (you need to have browser extension installed)
- Universal rendering with async server data
- Webpack 3 (development and production config)
- Hot Module Replacement
- Immutable reducer data
- Babel - static props, decorators
- PostCSS (with autoprefixing)
- Linting
- Git hooks - lint before push
- Tree shaking build
- Import SVGs as React components
- Internationalization
- Tests
Tested with node 8.
PLEASE NOTE if you downloaded a zip, after unpacking, make sure you copy hidden (dot) files as well. A lot of people forget to do so, and project won't run without Babel configuration in .babelrc
.
Clone or download, navigate to folder, and install dependencies:
npm install
Once dependencies are installed following npm tasks are availble:
start
- starts client app only in development mode, using webpack dev serverclient:dev
- same asstart
client:build
- builds client applicationclient:preview
- runs client application in production mode, using webpack dev server (use for local testing of the client production build)server:dev
- starts server app only in development mode (use for testing server responses)universal:dev
- runs both server and client in watch mode, automatically restarts server on changesuniversal:build
- builds both server and client
There are other tasks as well which shouldn't be used on their own.
npm start
# or
npm run client:dev
Visit http://localhost:8080/
from your browser of choice.
Server is visible from the local network as well.
Build will be placed in the build
folder.
npm run client:build
If your app is not running on the server root you should change publicPath
at two places.
In webpack.config.js
(ATM line 76):
output: {
path: buildPath,
publicPath: '/your-app/',
filename: 'app-[hash].js',
},
and in source/js/constants/routes
(line 1):
const publicPath = '/your-app/'; // Don't forget the trailing slash (`/`).
Development server will be available at http://localhost:8080/your-app/
.
This command will start webpack dev server, but with NODE_ENV
set to production
.
Everything will be minified and served.
Hot reload will not work, so you need to refresh the page manually after changing the code.
npm run client:preview
npm run universal:dev
Visit http://localhost:8080/
from your browser of choice.
Both server and client will be rebuilt on change. Again no hot module reload.
npm run universal:build
copy package.json
and build
folder to your production server
install only production dependencies and run server
npm install --production
node ./build/server.js
If you are not using server rendering first run:
node remove-universal.js
then you have to manually remove unused code from
source/js/config/store.js
which is marked with:
// Remove if you are not using server rendering.
You should remove unused tasks from package.json
and unused params from source/js/config/store.js
too.
Client app is going to work without this but it is better to remove them as they just create noise.
If anyone is willing to automate this, help will be greatly appreciated.
Modern browsers and IE10+.
These are known bugs that affect development mode only.
-
In some versions of Safari
cheap-eval-source-map
results in "Can't find variable: SockJS". To solve it changewebpack.config.js
:// from devtool: IS_PRODUCTION ? false : 'cheap-eval-source-map', // to devtool: IS_PRODUCTION ? false : 'source-map',
-
Hot module reload is not working in IE 10. To test the app in development mode you'll need to change
inline
tofalse
inwebpack/dev-server.js
inline: false, // Change to false for IE10 dev mode
ESLint is set up by extending eslint-config-airbnb, with some options overridden to our preferences.
npm run lint
Linting pre-push hook is not enabled by default. It will prevent the push if lint task fails, but you need to add it manually by running:
npm run hook-add
To remove it, run this task:
npm run hook-remove
Follow the example in source/js/server.js
.
High level concept - define list of sagas per route to resolve and store the data before returning response to the client.
- When defining a saga, based on
isServer
param return an action instead of yielding it. - In
server.js
import your sagas, and define express routes with saga dependencies. Just pass array of sagas tohandleRequest
method. If you want to pass URL params addreq.params
as the fourth param. - On store creation these sagas will be run on server when route is matched.
- Express server will wait for saga tasks to finish and preload data to client's redux store. If any of the tasks fail, store will be returned with initial data.
Please note that paths to images in CSS files are relative to source/css/index.css
as it imports all of the other .css
files.
.BackgroundImgExample {
background-image: url(../assets/img/book1.jpg);
}
Just import your .svg
files from the source/assets/svg/
folder, and you are good to go.
import CircleIcon from 'svg/circle.svg';
// then in your render
<CircleIcon />