├── .babelrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── app.json ├── package.json ├── public ├── favicon.ico └── index.html ├── server ├── app.js ├── index.js └── loader.js └── src ├── App.css ├── App.js ├── components ├── About │ └── About.js ├── Contacts │ └── Contacts.js ├── Container │ └── Container.js ├── Home │ └── Home.js ├── NavBar │ ├── NavBar.css │ └── NavBar.js ├── NotFound │ ├── NotFound.css │ └── NotFound.js └── index.js ├── index.css ├── index.js └── logo.svg /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "env", 5 | "react-app" 6 | ], 7 | "plugins": [ 8 | [ 9 | "babel-plugin-transform-require-ignore", 10 | { 11 | "extensions": [".css"] 12 | } 13 | ] 14 | ] 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | package-lock.json 19 | yarn.lock 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # testing 5 | /coverage 6 | 7 | # production 8 | /build 9 | 10 | # misc 11 | .DS_Store 12 | .env.local 13 | .env.development.local 14 | .env.test.local 15 | .env.production.local 16 | package-lock.json 17 | yarn.lock 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | .git* 24 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 6 5 | 6 | script: 7 | - npm run build 8 | 9 | cache: 10 | directories: 11 | - node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React.js Sample Project 2 | 3 | [![dependencies Status](https://david-dm.org/edoko/react-js-sample/status.svg)](https://david-dm.org/edoko/react-js-sample) [![devDependencies Status](https://david-dm.org/edoko/react-js-sample/dev-status.svg)](https://david-dm.org/edoko/react-js-sample?type=dev) [![npm version](https://badge.fury.io/js/react-js-sample.svg)](https://badge.fury.io/js/react-js-sample) [![Build Status](https://travis-ci.org/edoko/react-js-sample.svg?branch=master)](https://travis-ci.org/edoko/react-js-sample) 4 | 5 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/edoko/react-js/sample) 6 | 7 | 8 | [Live Demo](http://react-js-sample.herokuapp.com/) 9 | 10 | This app is built using React.js Boilerplate project. 11 | * React.js (create-react-app) 12 | * React Router v4 13 | * Server: Node.js (Express) 14 | * Support 404 Not found page 15 | * Support Server-side rendering 16 | 17 | ### Installation 18 | 19 | Clone repository: 20 | ```sh 21 | git clone git@github.com:edoko/react-js-sample.git 22 | ``` 23 | Before running: 24 | ```sh 25 | npm install 26 | ``` 27 | 28 | 29 | ## Run 30 | 31 | ### Local test 32 | 33 | ```sh 34 | npm start 35 | ``` 36 | 37 | ### Local+Server test 38 | 39 | ```sh 40 | npm run test 41 | ``` 42 | 43 | ### Server only test 44 | 45 | ```sh 46 | npm run server 47 | ``` 48 | 49 | ### Production build 50 | 51 | ```sh 52 | npm run build 53 | ``` -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "React.js Sample", 3 | "description": "React.js + React Router v4 + Express.js Sample Project", 4 | "repository": "https://github.com/edoko/react-js-sample", 5 | "logo": "http://react-js-sample.herokuapp.com/favicon.ico", 6 | "keywords": ["react", "reactjs", "reactrouter", "expressjs", "express", "boilerplate"] 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-js-sample", 3 | "version": "1.0.7", 4 | "description": "React.js + React Router v4 + Express.js(Server-side rendering) Boilerplate Project", 5 | "author": { 6 | "name": "Seongmin Park", 7 | "email": "me@komalab.io", 8 | "url": "http://komalab.io/" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/edoko/react-js-sample.git" 13 | }, 14 | "bugs": { 15 | "url": "http://github.com/edoko/react-js-sample/issues" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "http://www.opensource.org/licenses/MIT" 21 | } 22 | ], 23 | "keywords": [ 24 | "react", 25 | "reactjs", 26 | "reactrouter", 27 | "expressjs", 28 | "boilerplate", 29 | "reactsample" 30 | ], 31 | "dependencies": { 32 | "compression": "^1.7.0", 33 | "express": "^4.15.4", 34 | "morgan": "^1.8.2", 35 | "react": "^15.6.1", 36 | "react-dom": "^15.6.1", 37 | "react-router-dom": "^4.1.2", 38 | "react-scripts": "1.1.5" 39 | }, 40 | "scripts": { 41 | "start-win": "set NODE_ENV=production&babel-node server", 42 | "start-other": "export BABEL_ENV=production&&babel-node server", 43 | "build": "react-scripts build", 44 | "test": "react-scripts build && babel-node server", 45 | "server": "babel-node server", 46 | "eject": "react-scripts eject" 47 | }, 48 | "devDependencies": { 49 | "babel-cli": "^6.24.1", 50 | "babel-core": "^6.26.0", 51 | "babel-plugin-transform-require-ignore": "^0.1.1", 52 | "babel-preset-env": "^1.6.0", 53 | "babel-preset-es2015": "^6.24.1", 54 | "babel-preset-react-app": "^3.0.2", 55 | "babel-preset-stage-0": "^6.24.1" 56 | } 57 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edoko/react-js-sample/db53362c2ef5c55df2b5aa6efe0304456ff0c86d/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React.js + React Router v4 + Express.js Sample App 9 | 10 | 11 | 14 |
{{SSR}}
15 | 16 | 17 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | // Express.js server file (app) 2 | 3 | import express from 'express'; 4 | import morgan from 'morgan'; 5 | import path from 'path'; 6 | import compression from 'compression'; 7 | import loader from './loader'; 8 | 9 | const app = express(); 10 | 11 | // Support compress gzip format 12 | app.use(compression()); 13 | 14 | // request logger with morgan 15 | app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer"')); 16 | 17 | // server static file 18 | app.use(express.static(path.resolve(__dirname, '../build'))); 19 | 20 | app.use('/', loader); 21 | 22 | // export module 23 | export default app; -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | // Express.js server file (index) 2 | 3 | import app from './app'; 4 | 5 | // listen port 3000 6 | const PORT = process.env.PORT || 3000; 7 | 8 | app.listen(PORT, () => { 9 | console.log(`Listening on ${PORT}\nLaunch your browser!`); 10 | }); 11 | -------------------------------------------------------------------------------- /server/loader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import path from 'path'; 3 | import fs from 'fs'; 4 | import { renderToString } from 'react-dom/server'; 5 | import { StaticRouter } from 'react-router-dom'; 6 | import App from '../src/components/Container/Container'; 7 | 8 | export default (req, res) => { 9 | const filePath = path.resolve(__dirname, '../build', 'index.html'); 10 | 11 | fs.readFile(filePath, 'utf8', (err, htmlData)=>{ 12 | if (err) { 13 | console.error('Read Error!', err); 14 | return res.status(404).end(); 15 | } 16 | 17 | const context = {}; 18 | const markup = renderToString( 19 | 20 | 21 | 22 | ); 23 | 24 | if (context.url) { 25 | redirect(301, context.url); 26 | } else { 27 | // 리로드해도 정상적으로 렌더링 28 | const render = htmlData.replace('{{SSR}}', markup); 29 | res.send(render); 30 | } 31 | 32 | }); 33 | }; -------------------------------------------------------------------------------- /src/App.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/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Container, NavBar } from './components'; 3 | import logo from './logo.svg'; 4 | import './App.css'; 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
10 |
11 | logo 12 |

Welcome to React

13 |
14 |

15 | 16 | 17 |

18 |
19 | ); 20 | } 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /src/components/About/About.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class About extends Component { 4 | render() { 5 | return ( 6 |
7 |

This is About page.

8 |

Hi~~~

9 |
10 | ); 11 | } 12 | } 13 | 14 | export default About; -------------------------------------------------------------------------------- /src/components/Contacts/Contacts.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class Contacts extends Component { 4 | render() { 5 | return ( 6 |
7 |

This is Contacts page.

8 |

wluns32@gmail.com

9 |
10 | ); 11 | } 12 | } 13 | 14 | export default Contacts; -------------------------------------------------------------------------------- /src/components/Container/Container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Route, Switch } from 'react-router-dom' 3 | 4 | import { Home, About, Contacts, NotFound } from '../'; 5 | 6 | class Container extends Component { 7 | render() { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | ); 18 | } 19 | } 20 | 21 | export default Container; -------------------------------------------------------------------------------- /src/components/Home/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class Home extends Component { 4 | render() { 5 | return ( 6 |
7 |

This is Home page.

8 |

built using React.js + React Router v4 + Express.js

9 |

Now, It is Server-side rendering !

10 |
11 | ); 12 | } 13 | } 14 | 15 | export default Home; -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.css: -------------------------------------------------------------------------------- 1 | .NavList { 2 | text-align: center; 3 | margin-top: 5rem; 4 | } 5 | 6 | .NavList li { 7 | display: inline-block; 8 | padding: 0 5rem; 9 | font-weight: bold; 10 | font-size: 2rem; 11 | } -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import './NavBar.css'; 4 | 5 | class NavBar extends Component { 6 | render() { 7 | return ( 8 | 19 | ); 20 | } 21 | } 22 | 23 | export default NavBar; -------------------------------------------------------------------------------- /src/components/NotFound/NotFound.css: -------------------------------------------------------------------------------- 1 | .NotFound h1, h2 { 2 | text-align: center; 3 | } -------------------------------------------------------------------------------- /src/components/NotFound/NotFound.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './NotFound.css'; 3 | 4 | class NotFound extends Component { 5 | 6 | render() { 7 | return ( 8 |
9 |

404: NOT FOUND

10 |

You referred to the wrong address.

11 |
12 | ); 13 | } 14 | } 15 | 16 | export default NotFound; -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import NavBar from './NavBar/NavBar'; 2 | import Container from './Container/Container'; 3 | import About from './About/About'; 4 | import Contacts from './Contacts/Contacts'; 5 | import Home from './Home/Home'; 6 | import NotFound from './NotFound/NotFound'; 7 | 8 | export { 9 | NavBar, 10 | Container, 11 | About, 12 | Contacts, 13 | Home, 14 | NotFound 15 | }; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {BrowserRouter} from 'react-router-dom' 4 | import './index.css'; 5 | import App from './App'; 6 | 7 | const clientRender = () => { 8 | ReactDOM.render( 9 | , document.getElementById('root')); 10 | }; 11 | 12 | const serverRender = () => {}; 13 | 14 | if (typeof window === 'object') { 15 | clientRender(); 16 | } else { 17 | serverRender(); 18 | }; 19 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------