├── .eslintignore ├── .gitignore ├── .travis.yml ├── .editorconfig ├── examples ├── remount │ ├── components │ │ ├── app.jsx │ │ └── html.jsx │ ├── client.js │ ├── webpack.js │ └── server.js ├── layout │ ├── views │ │ ├── about.jsx │ │ ├── home.jsx │ │ └── layout.jsx │ └── server.js └── simple │ ├── views │ └── home.jsx │ └── server.js ├── test ├── fixtures │ ├── view.jsx │ └── view-precompiled.jsx └── index.js ├── LICENSE ├── package.json ├── index.js └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | examples/remount/assets 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | examples/remount/assets 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.10" 5 | - "0.11" 6 | - "0.12" 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # 4 space indentation 2 | [*.js] 3 | indent_style = space 4 | indent_size = 4 5 | [*.jsx] 6 | indent_style = space 7 | indent_size = 4 8 | -------------------------------------------------------------------------------- /examples/remount/components/app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | 4 | var Component = React.createClass({ 5 | render: function () { 6 | 7 | return ( 8 |
Foo: {this.props.foo}
9 | ); 10 | } 11 | }); 12 | 13 | 14 | module.exports = Component; 15 | -------------------------------------------------------------------------------- /examples/remount/client.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var AppComponent = require('./components/app.jsx'); 3 | 4 | 5 | var App = React.createFactory(AppComponent); 6 | var mountNode = document.getElementById('app-mount'); 7 | var serverState = window.state; 8 | 9 | 10 | React.render(App(serverState), mountNode); 11 | -------------------------------------------------------------------------------- /examples/layout/views/about.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Layout = require('./layout.jsx'); 3 | 4 | 5 | var Component = React.createClass({ 6 | render: function () { 7 | 8 | return ( 9 | 10 |

About the plot device.

11 |
12 | ); 13 | } 14 | }); 15 | 16 | 17 | module.exports = Component; 18 | -------------------------------------------------------------------------------- /examples/layout/views/home.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Layout = require('./layout.jsx'); 3 | 4 | 5 | var Component = React.createClass({ 6 | render: function () { 7 | 8 | return ( 9 | 10 |

Welcome to the plot device.

11 |
12 | ); 13 | } 14 | }); 15 | 16 | 17 | module.exports = Component; 18 | -------------------------------------------------------------------------------- /examples/remount/webpack.js: -------------------------------------------------------------------------------- 1 | var Path = require('path'); 2 | 3 | 4 | module.exports = { 5 | entry: Path.join(__dirname, './client.js'), 6 | resolve: { 7 | extensions: ['', '.js', '.jsx'] 8 | }, 9 | output: { 10 | filename: Path.join(__dirname, './assets/client.js') 11 | }, 12 | module: { 13 | loaders: [ 14 | { test: /\.jsx$/, loader: 'jsx-loader' } 15 | ] 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /test/fixtures/view.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | 4 | var Component = React.createClass({ 5 | render: function () { 6 | 7 | return ( 8 | 9 | 10 | {this.props.title} 11 | 12 | 13 |

Activate the plot device.

14 | 15 | 16 | ); 17 | } 18 | }); 19 | 20 | 21 | module.exports = Component; 22 | -------------------------------------------------------------------------------- /examples/simple/views/home.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | 4 | var Component = React.createClass({ 5 | render: function () { 6 | 7 | return ( 8 | 9 | 10 | {this.props.title} 11 | 12 | 13 |

Activate the plot device.

14 | 15 | 16 | ); 17 | } 18 | }); 19 | 20 | 21 | module.exports = Component; 22 | -------------------------------------------------------------------------------- /examples/layout/views/layout.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | 4 | var Component = React.createClass({ 5 | render: function () { 6 | 7 | return ( 8 | 9 | 10 | {this.props.title} 11 | 12 | 13 | {this.props.children} 14 |
15 |

16 | Home | About Us 17 |

18 | 19 | 20 | ); 21 | } 22 | }); 23 | 24 | 25 | module.exports = Component; 26 | -------------------------------------------------------------------------------- /examples/remount/components/html.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | 4 | var Component = React.createClass({ 5 | render: function () { 6 | 7 | return ( 8 | 9 | 10 | Remount Example 11 | 12 | 13 |
15 |
16 | 19 | 20 | 21 | 22 | ); 23 | } 24 | }); 25 | 26 | 27 | module.exports = Component; 28 | -------------------------------------------------------------------------------- /examples/simple/server.js: -------------------------------------------------------------------------------- 1 | var Hapi = require('hapi'); 2 | var Vision = require('vision'); 3 | var HapiReactViews = require('../..'); 4 | 5 | 6 | var server = new Hapi.Server(); 7 | server.connection(); 8 | server.register(Vision, function (err) { 9 | 10 | if (err) { 11 | console.log('Failed to load vision.'); 12 | } 13 | 14 | server.views({ 15 | engines: { 16 | jsx: HapiReactViews 17 | }, 18 | relativeTo: __dirname, 19 | path: 'views' 20 | }); 21 | 22 | server.route({ 23 | method: 'GET', 24 | path: '/', 25 | handler: function (request, reply) { 26 | 27 | reply.view('home'); 28 | } 29 | }); 30 | 31 | server.start(function (err) { 32 | 33 | if (err) { 34 | throw err; 35 | } 36 | 37 | console.log('Server is listening at ' + server.info.uri); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/fixtures/view-precompiled.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // precompiled with Babel (like when using the require hook for Babel) 3 | 4 | var React = require('react'); 5 | 6 | var Component = React.createClass({ 7 | displayName: 'Component', 8 | 9 | render: function render() { 10 | 11 | return React.createElement( 12 | 'html', 13 | null, 14 | React.createElement( 15 | 'head', 16 | null, 17 | React.createElement( 18 | 'title', 19 | null, 20 | this.props.title 21 | ) 22 | ), 23 | React.createElement( 24 | 'body', 25 | null, 26 | React.createElement( 27 | 'p', 28 | null, 29 | 'This is precompiled.' 30 | ) 31 | ) 32 | ); 33 | } 34 | }); 35 | 36 | module.exports = Component; -------------------------------------------------------------------------------- /examples/layout/server.js: -------------------------------------------------------------------------------- 1 | var Hapi = require('hapi'); 2 | var Vision = require('vision'); 3 | var HapiReactViews = require('../..'); 4 | 5 | 6 | var server = new Hapi.Server(); 7 | server.connection(); 8 | server.register(Vision, function (err) { 9 | 10 | if (err) { 11 | console.log('Failed to load vision.'); 12 | } 13 | 14 | server.views({ 15 | engines: { 16 | jsx: HapiReactViews 17 | }, 18 | relativeTo: __dirname, 19 | path: 'views' 20 | }); 21 | 22 | server.route({ 23 | method: 'GET', 24 | path: '/', 25 | handler: function (request, reply) { 26 | 27 | reply.view('home'); 28 | } 29 | }); 30 | 31 | server.route({ 32 | method: 'GET', 33 | path: '/about', 34 | handler: function (request, reply) { 35 | 36 | reply.view('about'); 37 | } 38 | }); 39 | 40 | server.start(function (err) { 41 | 42 | if (err) { 43 | throw err; 44 | } 45 | 46 | console.log('Server is listening at ' + server.info.uri); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2014 Reza Akhavan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-react-views", 3 | "version": "3.1.2", 4 | "description": "A hapi view engine for React components.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/lab/bin/lab -c -L", 8 | "test-cover": "./node_modules/lab/bin/lab -c -r html -o ./test/artifacts/coverage.html && open ./test/artifacts/coverage.html", 9 | "simple-example": "node ./examples/simple/server", 10 | "layout-example": "node ./examples/layout/server", 11 | "remount-example": "webpack --config ./examples/remount/webpack.js && node ./examples/remount/server" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/jedireza/hapi-react-views" 16 | }, 17 | "keywords": [ 18 | "hapi", 19 | "react", 20 | "views" 21 | ], 22 | "author": "Reza Akhavan (http://reza.akhavan.me/)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/jedireza/hapi-react-views/issues" 26 | }, 27 | "homepage": "https://github.com/jedireza/hapi-react-views", 28 | "dependencies": { 29 | "hoek": "^2.x.x", 30 | "node-jsx": "^0.13.x" 31 | }, 32 | "peerDependencies": { 33 | "react": "^0.14.x ", 34 | "react-dom": "^0.14.x " 35 | }, 36 | "devDependencies": { 37 | "code": "^1.x.x", 38 | "hapi": "^9.x.x", 39 | "inert": "^3.x.x", 40 | "jsx-loader": "^0.13.x", 41 | "lab": "^5.x.x", 42 | "react": "^0.14.x ", 43 | "react-dom": "^0.14.x ", 44 | "vision": "^3.x.x", 45 | "webpack": "^1.x.x" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var Hoek = require('hoek'); 2 | var React = require('react'); 3 | var ReactDOMServer = require('react-dom/server'); 4 | 5 | 6 | var EXT_REGEX = new RegExp('\\.jsx$'); 7 | var DEFAULTS = { 8 | doctype: '', 9 | renderMethod: 'renderToStaticMarkup', 10 | removeCache: process.env.NODE_ENV !== 'production', 11 | useNodeJsx: true, 12 | 'node-jsx': undefined 13 | }; 14 | 15 | 16 | var compile = function compile (template, compileOpts) { 17 | 18 | compileOpts = Hoek.applyToDefaults(DEFAULTS, compileOpts); 19 | var Component, Element; 20 | 21 | return function runtime (context, renderOpts) { 22 | 23 | renderOpts = Hoek.applyToDefaults(compileOpts, renderOpts); 24 | if (renderOpts.useNodeJsx === true) { 25 | require('node-jsx').install(compileOpts['node-jsx']); 26 | } 27 | var output = renderOpts.doctype; 28 | Component = Component || require(compileOpts.filename); 29 | Element = Element || React.createFactory(Component); 30 | output += ReactDOMServer[renderOpts.renderMethod](Element(context)); 31 | 32 | // node-jsx takes a long time to start up, so we delete 33 | // react modules from the cache so we don't need to restart 34 | // to see view changes (unless we're in production silly) 35 | if (renderOpts.removeCache) { 36 | Component = undefined; 37 | Element = undefined; 38 | 39 | Object.keys(require.cache).forEach(function (module) { 40 | 41 | if (EXT_REGEX.test(module)) { 42 | delete require.cache[module]; 43 | } 44 | }); 45 | } 46 | 47 | return output; 48 | }; 49 | }; 50 | 51 | 52 | module.exports = { 53 | compile: compile 54 | }; 55 | -------------------------------------------------------------------------------- /examples/remount/server.js: -------------------------------------------------------------------------------- 1 | var Path = require('path'); 2 | var Hapi = require('hapi'); 3 | var Inert = require('inert'); 4 | var Vision = require('vision'); 5 | var HapiReactViews = require('../..'); 6 | 7 | 8 | var server = new Hapi.Server(); 9 | server.connection(); 10 | server.register([Inert, Vision], function (err) { 11 | 12 | if (err) { 13 | console.log('Failed to load plugins.'); 14 | } 15 | 16 | server.views({ 17 | engines: { 18 | jsx: HapiReactViews 19 | }, 20 | relativeTo: __dirname, 21 | path: 'components' 22 | }); 23 | 24 | server.route({ 25 | method: 'GET', 26 | path: '/assets/client.js', 27 | handler: { 28 | file: Path.join(__dirname, './assets/client.js') 29 | } 30 | }); 31 | 32 | server.route({ 33 | method: 'GET', 34 | path: '/', 35 | handler: function (request, reply) { 36 | 37 | var appContext = { 38 | foo: 'bar' 39 | }; 40 | var renderOpts = { 41 | runtimeOptions: { 42 | renderMethod: 'renderToString' 43 | } 44 | }; 45 | 46 | server.render('app', appContext, renderOpts, function (err, appOutput) { 47 | 48 | var htmlContext = { 49 | remount: appOutput, 50 | state: 'window.state = ' + JSON.stringify(appContext) + ';' 51 | }; 52 | 53 | server.render('html', htmlContext, function (err, htmlOutput) { 54 | 55 | reply(htmlOutput); 56 | }); 57 | }); 58 | } 59 | }); 60 | 61 | server.start(function (err) { 62 | 63 | if (err) { 64 | throw err; 65 | } 66 | 67 | console.log('Server is listening at ' + server.info.uri); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var Lab = require('lab'); 2 | var Code = require('code'); 3 | var Hapi = require('hapi'); 4 | var Vision = require('vision'); 5 | var HapiReactViews = require('../index'); 6 | 7 | 8 | var lab = exports.lab = Lab.script(); 9 | 10 | 11 | lab.experiment('Engine', function () { 12 | 13 | lab.test('it is an object with a compile method', function (done) { 14 | 15 | Code.expect(HapiReactViews).to.be.an.object(); 16 | Code.expect(HapiReactViews.compile).to.be.a.function(); 17 | done(); 18 | }); 19 | }); 20 | 21 | 22 | lab.experiment('Rendering', function () { 23 | 24 | var server; 25 | 26 | lab.beforeEach(function (done) { 27 | 28 | server = new Hapi.Server(0); 29 | 30 | server.register(Vision, function (err) { 31 | 32 | if (err) { 33 | return done(err); 34 | } 35 | 36 | server.views({ 37 | engines: { 38 | jsx: HapiReactViews 39 | }, 40 | relativeTo: __dirname, 41 | path: 'fixtures' 42 | }); 43 | 44 | done(); 45 | }); 46 | }); 47 | 48 | 49 | lab.test('it successfully renders with jsx compilation turned off', function (done) { 50 | 51 | var context = { title: 'Woot, with no jsx options.' }; 52 | var renderOpts = { 53 | runtimeOptions: { 54 | useNodeJsx: false 55 | } 56 | }; 57 | 58 | server.render('view-precompiled', context, renderOpts, function (err, output) { 59 | 60 | Code.expect(err).to.not.exist(); 61 | done(); 62 | }); 63 | }); 64 | 65 | 66 | lab.test('it fails if rendering jsx with jsx compilation turned off', function (done) { 67 | 68 | var context = { title: 'Ooops, node-jsx is disabled.' }; 69 | var renderOpts = { 70 | runtimeOptions: { 71 | useNodeJsx: false 72 | } 73 | }; 74 | 75 | server.render('view', context, renderOpts, function (err, output) { 76 | 77 | Code.expect(err).to.exist(); 78 | done(); 79 | }); 80 | }); 81 | 82 | 83 | 84 | lab.test('it returns an error when the path misses', function (done) { 85 | 86 | var context = { title: 'Woops.' }; 87 | 88 | server.render('viewz', context, function (err, output) { 89 | 90 | Code.expect(err).to.be.an.object(); 91 | done(); 92 | }); 93 | }); 94 | 95 | 96 | lab.test('it successfully renders', function (done) { 97 | 98 | var context = { title: 'Woot, it rendered.' }; 99 | 100 | server.render('view', context, function (err, output) { 101 | 102 | Code.expect(err).to.not.exist(); 103 | done(); 104 | }); 105 | }); 106 | 107 | 108 | lab.test('it successfully renders with runtime options', function (done) { 109 | 110 | var context = { title: 'Woot, with runtime options.' }; 111 | var renderOpts = { 112 | runtimeOptions: { 113 | doctype: '', 114 | renderMethod: 'renderToString' 115 | } 116 | }; 117 | 118 | server.render('view', context, renderOpts, function (err, output) { 119 | 120 | Code.expect(err).to.not.exist(); 121 | done(); 122 | }); 123 | }); 124 | 125 | 126 | lab.test('it demonstrates caching', function (done) { 127 | 128 | var context = { title: 'Woot, it rendered.' }; 129 | var renderOpts = { 130 | runtimeOptions: { 131 | removeCache: false 132 | } 133 | }; 134 | 135 | server.render('view', context, renderOpts, function (err, output) { 136 | 137 | Code.expect(err).to.not.exist(); 138 | 139 | server.render('view', context, renderOpts, function (err, out) { 140 | 141 | Code.expect(err).to.not.exist(); 142 | done(); 143 | }); 144 | }); 145 | }); 146 | }); 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hapi-react-views 2 | 3 | A hapi view engine for React components. 4 | 5 | [![Build Status](https://travis-ci.org/jedireza/hapi-react-views.svg?branch=master)](https://travis-ci.org/jedireza/hapi-react-views) 6 | [![Dependency Status](https://david-dm.org/jedireza/hapi-react-views.svg?theme=shields.io)](https://david-dm.org/jedireza/hapi-react-views) 7 | [![Peer Dependency Status](https://david-dm.org/jedireza/hapi-react-views/peer-status.svg?style=flat)](https://david-dm.org/jedireza/hapi-react-views#info=peerDependencies) 8 | [![Dev Dependency Status](https://david-dm.org/jedireza/hapi-react-views/dev-status.svg?theme=shields.io)](https://david-dm.org/jedireza/hapi-react-views#info=devDependencies) 9 | 10 | By default, we render static markup. We can also choose to use 11 | `React.renderToString`, preserving the `data-react-id` attributes so 12 | re-mounting client side is possible. 13 | 14 | 15 | ## Install 16 | 17 | ```bash 18 | $ npm install hapi-react-views 19 | ``` 20 | 21 | Note: Your project should have it's own `react` dependency installed. We depend 22 | on `react` via `peerDependencies`. 23 | 24 | 25 | ## Usage 26 | 27 | Note: As of hapi v9.x, your project must register the 28 | [`vision`](https://github.com/hapijs/vision) plugin in order for the 29 | `server.views()` method to be available. 30 | 31 | Configuring the server manually: 32 | 33 | ```js 34 | var Hapi = require('hapi'); 35 | var Vision = require('vision'); 36 | var HapiReactViews = require('hapi-react-views'); 37 | 38 | var server = new Hapi.Server(); 39 | 40 | server.register(Vision, function (err) { 41 | 42 | if (err) { 43 | console.log("Failed to load vision."); 44 | } 45 | 46 | server.views({ 47 | engines: { 48 | jsx: HapiReactViews 49 | }, 50 | compileOptions: {}, // optional 51 | relativeTo: __dirname, 52 | path: 'views' 53 | }); 54 | }); 55 | ``` 56 | 57 | Configuring with a CLI manifest using 58 | [`visionary`](https://github.com/hapijs/visionary): 59 | 60 | ```json 61 | { 62 | "servers": [{ 63 | "port": 8080 64 | }], 65 | "plugins": { 66 | "vision": {}, 67 | "visionary": { 68 | "engines": { 69 | "jsx": "hapi-react-views" 70 | }, 71 | "compileOptions": {}, 72 | "path": "./views" 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | 79 | ## API 80 | 81 | ### `server.views(options)` 82 | 83 | [Please refer to the `vision` docs on 84 | `server.views(options)` for complete details.](https://github.com/hapijs/vision/blob/master/API.md#serverviewsoptions) 85 | 86 | We'll be focusing on the `compileOptions` property that you can include when 87 | passing `options` to `server.views`. 88 | 89 | The following `compileOptions` will customize how `hapi-react-views` works. 90 | 91 | - `compileOptions` - options object passed to the engine's compile function. 92 | Defaults to `{}`. 93 | - `doctype` - a simple string prepended to the response. Defaults to 94 | `` 95 | - `renderMethod` - the method to invoke on `React` to generate our output. 96 | Available options are `renderToStaticMarkup` and `renderToString`. 97 | Defaults to `renderToStaticMarkup`. 98 | - `removeCache` - since `node-jsx` takes a while to startup, we can remove 99 | templates from the cache so we don't need to restart the server to see 100 | changes. Defaults to `'production' !== process.env.NODE_ENV`. 101 | - `useNodeJsx` - a boolean that controls if `node-jsx` is used. Defaults to 102 | `true`. Set to `false` if you're using another transformer (ex: 103 | `babel/require`) or don't need `jsx` transformations. 104 | - `node-jsx` - options object passed to 105 | [`node-jsx`](https://github.com/petehunt/node-jsx)'s `install` method. 106 | Defaults to `undefined`. 107 | 108 | You're able to override all these `compileOptions` at runtime except `node-jsx` 109 | which only happens once. 110 | 111 | ```js 112 | var context = { name: 'Steve' }; 113 | var renderOpts = { 114 | runtimeOptions: { 115 | doctype: '', 116 | renderMethod: 'renderToString' 117 | } 118 | }; 119 | 120 | server.render('template', context, renderOpts, function (err, output) { 121 | 122 | // ... 123 | }); 124 | ``` 125 | 126 | [Please refer to `vision`'s docs on 127 | `server.render(template, context, [options], callback)` for complete details.](https://github.com/hapijs/vision/blob/master/API.md#serverrendertemplate-context-options-callback) 128 | 129 | 130 | ## Examples 131 | 132 | Before you can run the examples, you need to clone this repo and install the dependencies. 133 | 134 | ```bash 135 | $ git clone git@github.com:jedireza/hapi-react-views.git 136 | $ cd hapi-react-views 137 | $ npm install 138 | ``` 139 | 140 | ### Rendering a simple page 141 | 142 | This example renders a simple component as HTML output. [View the 143 | code.](https://github.com/jedireza/hapi-react-views/tree/master/examples/simple) 144 | 145 | ```bash 146 | $ npm run simple-example 147 | ``` 148 | 149 | ### Rendering with layouts 150 | 151 | This example is renders simple components as HTML but adds the idea of using 152 | layouts. [View the 153 | code.](https://github.com/jedireza/hapi-react-views/tree/master/examples/layout) 154 | 155 | ```bash 156 | $ npm run layout-example 157 | ``` 158 | 159 | ### Remounting on the client (universal/isomorphic) 160 | 161 | This example demonstrates the idea of remounting client side in order to create 162 | universal/isomorphic applications. [View the 163 | code.](https://github.com/jedireza/hapi-react-views/tree/master/examples/remount) 164 | 165 | ```bash 166 | $ npm run remount-example 167 | ``` 168 | 169 | 170 | ## License 171 | 172 | MIT 173 | 174 | 175 | ## Don't forget 176 | 177 | What you create with `hapi-react-views` is more important than `hapi-react-views`. 178 | --------------------------------------------------------------------------------