├── .gitignore ├── assets └── style.css ├── package.json ├── server.js ├── README.md └── client.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | assets/bundle.js 3 | -------------------------------------------------------------------------------- /assets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: Helvetica, sans-serif; 5 | background: #fefefe; 6 | color: #444444; 7 | } 8 | 9 | .App { 10 | padding: 25px; 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-quickstart", 3 | "version": "0.0.0", 4 | "description": "React project", 5 | "main": "./server.js", 6 | "browser": "./client.js", 7 | "browserify": { 8 | "transform": [ 9 | ["reactify", {"harmony": true}] 10 | ] 11 | }, 12 | "dependencies": { 13 | "react": "~0.10.0", 14 | "react-async": "~0.9.1", 15 | "react-router-component": "~0.18.2", 16 | "express": "~4.1.1", 17 | "node-jsx": "~0.10.0", 18 | "superagent": "~0.18.0", 19 | "fibers": "~1.0.1" 20 | }, 21 | "devDependencies": { 22 | "reactify": "~0.13.1", 23 | "envify": "~1.2.0", 24 | "browserify": "~3.44.2", 25 | "connect-browserify": "~2.0.1", 26 | "uglify-js": "~2.4.13", 27 | "supervisor": "~0.6.0" 28 | }, 29 | "scripts": { 30 | "test": "echo \"Error: no test specified\" && exit 1", 31 | "start": "supervisor -i node_modules -e js,jsx server.js", 32 | "build": "NODE_ENV=production browserify ./ | uglifyjs -cm 2>/dev/null > ./assets/bundle.js", 33 | "start-prod": "NODE_ENV=production node server.js", 34 | "clean": "rm -f ./assets/bundle.js" 35 | }, 36 | "author": "", 37 | "license": "MIT" 38 | } 39 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var url = require('url'); 5 | var express = require('express'); 6 | var browserify = require('connect-browserify'); 7 | var ReactAsync = require('react-async'); 8 | var nodejsx = require('node-jsx').install(); 9 | var App = require('./client'); 10 | 11 | var development = process.env.NODE_ENV !== 'production'; 12 | 13 | function renderApp(req, res, next) { 14 | var path = url.parse(req.url).pathname; 15 | var app = App({path: path}); 16 | ReactAsync.renderComponentToStringWithAsyncState(app, function(err, markup) { 17 | if (err) { 18 | return next(err); 19 | } 20 | res.send('\n' + markup); 21 | }); 22 | } 23 | 24 | var api = express() 25 | .get('/users/:username', function(req, res) { 26 | var username = req.params.username; 27 | res.send({ 28 | username: username, 29 | name: username.charAt(0).toUpperCase() + username.slice(1) 30 | }); 31 | }); 32 | 33 | var app = express(); 34 | 35 | if (development) { 36 | app.get('/assets/bundle.js', 37 | browserify('./client', { 38 | debug: true, 39 | watch: true 40 | })); 41 | } 42 | 43 | app 44 | .use('/assets', express.static(path.join(__dirname, 'assets'))) 45 | .use('/api', api) 46 | .use(renderApp) 47 | .listen(3000, function() { 48 | console.log('Point your browser at http://localhost:3000'); 49 | }); 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-quickstart 2 | 3 | A minimal React project template which combines: 4 | 5 | * [react-router-component][] to provide HTML5 History routing and navigation 6 | 7 | * [react-async][] to create "asynchronous" React components 8 | 9 | * [express][] to serve pre-rendered React components, assets and provide API 10 | 11 | * [browserify][] to provide module system for a browser 12 | 13 | * [npm][] to install and manage server-side and client-side dependencies 14 | 15 | Every "page" in the application is **pre-rendered on server** so the user can 16 | see the UI before the client code is shipped to a browser. After that 17 | application starts functioning like a **single page application**, navigating 18 | between "pages" without reloads. 19 | 20 | ## Project structure 21 | 22 | Project structure is really minimal, you'd probably like to customize it for 23 | your specific needs and taste: 24 | 25 | . 26 | ├── assets 27 | ├── client.js 28 | ├── package.json 29 | └── server.js 30 | 31 | Directory `assets` is served under `/assets` URL, `client.js` module contains UI 32 | code while `server.js` — HTTP server which serves pre-rendered React components, 33 | assets and provide a stub for a REST API. 34 | 35 | ## Development workflow 36 | 37 | After cloning a git repo, run: 38 | 39 | % npm install 40 | 41 | to install all needed dependencies and then: 42 | 43 | % npm run start 44 | 45 | to start a development server. 46 | 47 | Now you can start edit the source code — on changes, server will be reloaded and 48 | client code bundle will be rebuilt. 49 | 50 | ## Going "production" 51 | 52 | To build an optimized bundle of client code run: 53 | 54 | % npm run build 55 | 56 | which will produce `assets/bundle.js` build, then: 57 | 58 | % npm run start-prod 59 | 60 | to start server in "production" mode (no source code watching and serving 61 | optimized bundle to browser). 62 | 63 | [react-router-component]: http://andreypopp.viewdocs.io/react-router-component 64 | [react-async]: http://andreypopp.viewdocs.io/react-async 65 | [express]: expressjs.com 66 | [npm]: https://www.npmjs.org/ 67 | [browserify]: http://browserify.org/ 68 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jsx React.DOM 3 | */ 4 | 'use strict'; 5 | 6 | 7 | var React = require('react'); 8 | var ReactAsync = require('react-async'); 9 | var ReactRouter = require('react-router-component'); 10 | var superagent = require('superagent'); 11 | 12 | var Pages = ReactRouter.Pages; 13 | var Page = ReactRouter.Page; 14 | var NotFound = ReactRouter.NotFound; 15 | var Link = ReactRouter.Link; 16 | 17 | var MainPage = React.createClass({ 18 | 19 | render: function() { 20 | return ( 21 |
Login
24 |73 | Go to /users/{otherUser} 74 |
75 |Logout
76 |Page not found
86 | ); 87 | } 88 | }); 89 | 90 | var App = React.createClass({ 91 | 92 | render: function() { 93 | return ( 94 | 95 | 96 | 97 | 98 | 99 |