├── .babelrc
├── .gitignore
├── README.md
├── client-render.js
├── components
├── about.js
├── app.js
└── index.js
├── package.json
├── routes.js
├── server.js
├── views
└── index.ejs
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | public/build.js
2 | node_modules
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # universal-react-example
2 | An example Universal ReactJS application
3 |
4 | Written for 24ways. [Go read the blog post](https://24ways.org/2015/universal-react/) :)
5 |
6 | ## Install
7 | ```sh
8 | $ git clone https://github.com/jackfranklin/universal-react-example.git
9 | $ cd universal-react-example
10 | $ npm install
11 | $ npm run build
12 | $ npm start
13 | $ open http://localhost:3003
14 | ```
15 |
--------------------------------------------------------------------------------
/client-render.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Router, browserHistory } from 'react-router';
4 |
5 | import { routes } from './routes';
6 |
7 | // import createBrowserHistory from 'history/lib/createBrowserHistory';
8 |
9 | ReactDOM.render(
10 | ,
11 | document.getElementById('app')
12 | )
13 |
--------------------------------------------------------------------------------
/components/about.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class AboutComponent extends React.Component {
4 | render() {
5 | return (
6 |
7 |
A little bit about me.
8 |
9 | );
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/components/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | export default class AppComponent extends React.Component {
5 | render() {
6 | return (
7 |
8 |
Welcome to my App
9 |
10 | - Home
11 | - About
12 |
13 | { this.props.children }
14 |
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/components/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class IndexComponent extends React.Component {
4 | render() {
5 | return (
6 |
7 |
This is the index page
8 |
9 | );
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "universal-react-example",
3 | "version": "1.0.0",
4 | "description": "Universal react example",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "webpack -p",
9 | "start": "babel-node server.js"
10 | },
11 | "keywords": [
12 | "react",
13 | "react-router",
14 | "ejs",
15 | "express",
16 | "history",
17 | "react-dom",
18 | "babel",
19 | "webpack"
20 | ],
21 | "author": "",
22 | "license": "ISC",
23 | "dependencies": {
24 | "ejs": "2.4.2",
25 | "express": "4.14.0",
26 | "history": "3.0.0",
27 | "react": "15.2.1",
28 | "react-dom": "15.2.1",
29 | "react-router": "2.5.2"
30 | },
31 | "devDependencies": {
32 | "babel-cli": "6.10.1",
33 | "babel-core": "6.10.4",
34 | "babel-loader": "6.2.4",
35 | "babel-preset-es2015": "6.9.0",
36 | "babel-preset-react": "6.11.1",
37 | "webpack": "1.13.1"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/routes.js:
--------------------------------------------------------------------------------
1 | import AppComponent from './components/app';
2 | import IndexComponent from './components/index';
3 | import AboutComponent from './components/about';
4 |
5 | const routes = {
6 | path: '',
7 | component: AppComponent,
8 | childRoutes: [
9 | {
10 | path: '/',
11 | component: IndexComponent
12 | },
13 | {
14 | path: '/about',
15 | component: AboutComponent
16 | }
17 | ]
18 | }
19 |
20 | export { routes };
21 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 |
3 | import React from 'react';
4 | import { renderToString } from 'react-dom/server';
5 | import { match, RouterContext } from 'react-router';
6 |
7 | import { routes } from './routes';
8 |
9 | const app = express();
10 |
11 | app.use(express.static('public'));
12 |
13 | app.set('view engine', 'ejs');
14 |
15 | app.get('*', (req, res) => {
16 | // routes is our object of React routes defined above
17 | match({ routes, location: req.url }, (err, redirectLocation, props) => {
18 | if (err) {
19 | // something went badly wrong, so 500 with a message
20 | res.status(500).send(err.message);
21 | } else if (redirectLocation) {
22 | // we matched a ReactRouter redirect, so redirect from the server
23 | res.redirect(302, redirectLocation.pathname + redirectLocation.search);
24 | } else if (props) {
25 | // if we got props, that means we found a valid component to render
26 | // for the given route
27 | const markup = renderToString();
28 |
29 | // render `index.ejs`, but pass in the markup we want it to display
30 | res.render('index', { markup })
31 |
32 | } else {
33 | // no route match, so 404. In a real app you might render a custom
34 | // 404 view here
35 | res.sendStatus(404);
36 | }
37 | });
38 | });
39 | app.listen(3003, 'localhost', function(err) {
40 | if (err) {
41 | console.log(err);
42 | return;
43 | }
44 | console.log('Listening at http://localhost:3003');
45 | });
46 |
--------------------------------------------------------------------------------
/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | My App
5 |
6 |
7 |
8 | <%- markup %>
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | module.exports = {
3 | entry: path.join(process.cwd(), 'client-render.js'),
4 | output: {
5 | path: './public/',
6 | filename: 'build.js'
7 | },
8 | module: {
9 | loaders: [
10 | {
11 | test: /\.js$/,
12 | loader: 'babel'
13 | }
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------