├── .gitignore ├── Dockerfile ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── server ├── app.js └── index.js ├── src ├── components │ ├── About │ │ ├── index.js │ │ └── style.css │ ├── App │ │ ├── index.js │ │ ├── logo.svg │ │ └── style.css │ └── NotFound │ │ ├── index.js │ │ └── style.css ├── index.css ├── index.js └── routes.js └── test └── server.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile 2 | FROM node:6 3 | 4 | # Create app directory 5 | RUN mkdir -p /usr/src/app 6 | WORKDIR /usr/src/app 7 | 8 | # Install app dependencies 9 | COPY package.json /usr/src/app/ 10 | RUN npm install 11 | 12 | # Bundle app source 13 | COPY . /usr/src/app 14 | 15 | # Build and optimize react app 16 | RUN npm run build 17 | 18 | EXPOSE 9000 19 | 20 | # defined in package.json 21 | CMD [ "npm", "run", "start:server" ] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using `create-react-app` with React Router + Express.js 2 | 3 | **:warning: This is outdated, please refer to the official and new [react-router docs](https://reacttraining.com/react-router/)** 4 | 5 | See : https://medium.com/@patriciolpezjuri/using-create-react-app-with-react-router-express-js-8fa658bf892d 6 | 7 | ## Development 8 | 9 | Clone this repository: 10 | 11 | ```sh 12 | git clone https://github.com/mrpatiwi/routed-react.git 13 | cd routed-react 14 | ``` 15 | 16 | Install dependencies: 17 | 18 | ```sh 19 | npm install 20 | ``` 21 | 22 | Start the project at [`http://localhost:3000`](http://localhost:3000). 23 | 24 | ```sh 25 | npm start 26 | ``` 27 | 28 | ## Running with Docker 29 | 30 | Be sure to install Docker and start a Docker-machine if necessary. 31 | 32 | Let's create an image named `routed-react`: 33 | 34 | ```sh 35 | docker build -t routed-react . 36 | ``` 37 | 38 | Finally, start a container named `routed-react-instance` at port `80`. 39 | 40 | ```sh 41 | docker run -p 80:9000 --name routed-react-instance routed-react 42 | ``` 43 | 44 | ## Testing 45 | 46 | ```sh 47 | npm test 48 | ``` 49 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lopezjurip/routed-react/ab1b41dfd88df44221d9532fd73e1b61e815f48f/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "routed-react", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "chai": "^3.5.0", 7 | "mocha": "^3.0.2", 8 | "mz": "^2.4.0", 9 | "react-scripts": "0.2.1", 10 | "supertest-as-promised": "^4.0.0" 11 | }, 12 | "dependencies": { 13 | "classnames": "^2.2.5", 14 | "express": "^4.14.0", 15 | "morgan": "^1.7.0", 16 | "react": "^15.2.1", 17 | "react-dom": "^15.2.1", 18 | "react-router": "^2.6.1" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "start:server": "node server", 23 | "build": "react-scripts build", 24 | "eject": "react-scripts eject", 25 | "test": "mocha test" 26 | }, 27 | "eslintConfig": { 28 | "extends": "./node_modules/react-scripts/config/eslint.js", 29 | "env": { 30 | "mocha": true 31 | }, 32 | "rules": { 33 | "strict": 0 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | // server/app.js 2 | const express = require('express'); 3 | const morgan = require('morgan'); 4 | const path = require('path'); 5 | 6 | const app = express(); 7 | 8 | // Setup logger 9 | app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms')); 10 | 11 | // Serve static assets 12 | app.use(express.static(path.resolve(__dirname, '..', 'build'))); 13 | 14 | // Always return the main index.html, so react-router render the route in the client 15 | app.get('*', (req, res) => { 16 | res.sendFile(path.resolve(__dirname, '..', 'build', 'index.html')); 17 | }); 18 | 19 | module.exports = app; 20 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | // server/index.js 2 | 'use strict'; 3 | 4 | const app = require('./app'); 5 | 6 | const PORT = process.env.PORT || 9000; 7 | 8 | app.listen(PORT, () => { 9 | console.log(`App listening on port ${PORT}!`); 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/About/index.js: -------------------------------------------------------------------------------- 1 | // src/components/About/index.js 2 | import React, { Component } from 'react'; 3 | import classnames from 'classnames'; 4 | 5 | import './style.css'; 6 | 7 | class About extends Component { 8 | static propTypes = {} 9 | static defaultProps = {} 10 | state = {} 11 | 12 | render() { 13 | const { className, ...props } = this.props; 14 | return ( 15 |
16 |

17 | About 18 |

19 |
20 | ); 21 | } 22 | } 23 | 24 | export default About; 25 | -------------------------------------------------------------------------------- /src/components/About/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lopezjurip/routed-react/ab1b41dfd88df44221d9532fd73e1b61e815f48f/src/components/About/style.css -------------------------------------------------------------------------------- /src/components/App/index.js: -------------------------------------------------------------------------------- 1 | // src/components/App/index.js 2 | import React, { Component } from 'react'; 3 | import classnames from 'classnames'; 4 | 5 | import logo from './logo.svg'; 6 | import './style.css'; 7 | 8 | class App extends Component { 9 | static propTypes = {} 10 | static defaultProps = {} 11 | state = {} 12 | 13 | render() { 14 | const { className, ...props } = this.props; 15 | return ( 16 |
17 |
18 | logo 19 |

Welcome to React

20 |
21 |

22 | To get started, edit src/App.js and save to reload. 23 |

24 |
25 | ); 26 | } 27 | } 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /src/components/App/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/App/style.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/NotFound/index.js: -------------------------------------------------------------------------------- 1 | // src/components/NotFound/index.js 2 | import React, { Component } from 'react'; 3 | import classnames from 'classnames'; 4 | 5 | import './style.css'; 6 | 7 | export default class NotFound extends Component { 8 | static propTypes = {} 9 | static defaultProps = {} 10 | state = {} 11 | 12 | render() { 13 | const { className, ...props } = this.props; 14 | return ( 15 |
16 |

17 | 404 Not Found :( 18 |

19 |
20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/NotFound/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lopezjurip/routed-react/ab1b41dfd88df44221d9532fd73e1b61e815f48f/src/components/NotFound/style.css -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // index.js 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { browserHistory } from 'react-router' 5 | 6 | import Routes from './routes'; 7 | 8 | import './index.css'; 9 | 10 | ReactDOM.render( 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | // src/routes.js 2 | import React from 'react'; 3 | import { Router, Route } from 'react-router' 4 | 5 | import App from './components/App'; 6 | import About from './components/About'; 7 | import NotFound from './components/NotFound'; 8 | 9 | const Routes = (props) => ( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | 17 | export default Routes; 18 | -------------------------------------------------------------------------------- /test/server.test.js: -------------------------------------------------------------------------------- 1 | // test/server.test.js 2 | const exec = require('mz/child_process').exec; 3 | const request = require('supertest-as-promised'); 4 | const expect = require('chai').expect; 5 | 6 | const app = require('../server/app'); 7 | 8 | describe('builds application', function () { 9 | it('builds to "build" directory', function () { 10 | // Disable mocha time-out because this takes a lot of time 11 | this.timeout(0); 12 | 13 | // Run process 14 | return exec('npm run build'); 15 | }); 16 | }); 17 | 18 | describe('express serving', function () { 19 | it('responds to / with the index.html', function () { 20 | return request(app) 21 | .get('/') 22 | .expect('Content-Type', /html/) 23 | .expect(200) 24 | .then(res => expect(res.text).to.contain('
')); 25 | }); 26 | 27 | it('responds to favicon.icon request', function () { 28 | return request(app) 29 | .get('/favicon.ico') 30 | .expect('Content-Type', 'image/x-icon') 31 | .expect(200); 32 | }); 33 | 34 | it('responds to any route with the index.html', function () { 35 | return request(app) 36 | .get('/foo/bar') 37 | .expect('Content-Type', /html/) 38 | .expect(200) 39 | .then(res => expect(res.text).to.contain('
')); 40 | }); 41 | }); 42 | --------------------------------------------------------------------------------