react-response
v0.1.0
Published
React server-side rendering implementation with minimal configuration.
Downloads
29
Maintainers
Readme
React-response
React-response provides an easy-to-use server-side rendering server for React. The goal of this project is to reduce the boilerplate you need for a universal React project. Instead of copy-pasting a server from somewhere, React-response makes it possible to quickly write a production-ready server-side React renderer yourself. React-response also aims to maintain the customizability of a normal Express server because every app has different needs.
The configuration of your React-response is done with familiar React components, kind of like React Router's route configuration. Almost all of the props have sensible defaults, so for the simplest apps you really don't have to do a lot to get a server running.
Production-ready stability is one of my end goals but we're still in the early days. Use in production at your own risk!
Features
- A new way to implement SSR using familiar React concepts
- A fully-featured Express server under the hood
- Less boilerplate
What's it look like?
Glad you asked. The simplest hello World with React-response looks like this:
import routes from './routes' // React-router routes
import Html from './helpers/Html' // Your template component
import { ReactServer, Template, Response, serve, createServer } from 'react-response'
const server = createServer(
<ReactServer>
<Template component={ Html }>
<Response routes={ routes } />
</Template>
</ReactServer>
)
serve(server)
Compared to the novel you had to write (or copy-paste from a boilerplate) before, I'd say that's a pretty good improvement!
As you may have noticed, the <Response />
component consumes React Router routes. Yes, React Router is a peerDependency, along with React, React DOM and Express. I assume that most projects that need server-side rendering already uses React Router.
React-response is based on Express. If you're not using Express, rest assured that it is not suuuuper tightly coupled. I might eventually look at having interchangeable servers, but it is not a priority for now.
That small example is all well and good for showing off, but next we'll have a look at how to customize React-response for your needs.
The full example
import { RouterContext } from 'react-router' // Import if using a custom render function
import routes from './routes' // React-router routes
import Html from './helpers/Html' // Your template component
import { ReactServer, Template, Route, Response, serve, createServer, Middleware, Static, Favicon } from 'react-response'
/* Note that you need to install 'serve-favicon' and other middleware if you want to use them. */
const server = createServer(
<ReactServer host="localhost" port={ 3000 }>
<Route path="/" method="get">
// All the middlewares! React-response ships with some commonly used ones.
<Middleware use={ compression() }/>
<Favicon path={ path.join(__dirname, '..', 'static', 'favicon.ico') }/>
<Static path={ path.join(__dirname, '..', 'static') }/>
// Set your template component
<Template component={ Html }>
<Response routes={ routes }>
// Custom render function for your Reduxes and whatnot
{(renderProps, req, res) => {
// Return props for the template component
return { component: ReactDOM.renderToString(
<RouterContext { ...renderProps } />
) }
}}
</Response>
</Template>
</Route>
<Route path="/api"> // many routes
<Static path={ path.join(__dirname, '..', 'static') }/>
</Route>
</ReactServer>
)
serve(server)
Alright, this is more like it! As you can see, with React-response we attach middleware and app renderers to Routes, denoted by the <Route />
component. This is, as we saw in the simple example, completely optional.
Express middleware is painless to use through the <Middleware />
component. The middleware will be mounted on the route which the middleware component is a child of. Simply pass in a middleware function as the use
prop. Favicon
and Static
middleware components ship with React-response. They are simple wrappers for the generic middleware component.
The <Response />
component will handle rendering of React Router's <RouterContext />
by default. If you need to render something else, for example Redux's <Provider>
, pass a callback function as a child to <Response />
and take care of it there. If you use a custom render function, it is your responsibility to render the React component to a string.
The return value from your custom render function should be a map of props that will be applied to your template component. This makes the render function a convenient place to fetch data and create your Redux store.
The whole React-response setup is fed into the createServer
function. It traverses all the components and compiles a fully-featured Express server which you can feed to the serve
function.
A note on JSX
I know that some developers are not fond of JSX and prefer vanilla Javascript. My decision to use JSX and even React components for server configuration is bound to raise eyebrows. For me it comes down to preference, usability and how it looks. React-response is all about eliminating React boilerplate and easing the cognitive load of writing a server-side rendering server, so the last thing I wanted was a declarative and messy configuration.
The very first thing I did with React-response was to design the user interface of the configuration. While the data is not naturally nested like React Router's routes, I feel that using JSX and React components to build the server configuration gives React-response a distinct "React identity". Rendering React components on the server should just be a matter of composing them into the server configuration. It is also very easy to see what is going on from a quick glance at the configuration tree, and in my opinion it is much better than plain Javascript objects.
However, if you do not wish to use JSX, inspect the output of createServer
. It should be rather simple to re-create that object structure without JSX. Note that the components, like Middleware
and Response
, directly apply middleware and handlers to the Express instance.
Rest assured that I plan to fully support usage of React-response without React components, à la React Router. It just isn't a priority for the first few releases.
Getting started
First, install React-response:
npm install --save react-response
Make sure you have React-response's peerDependencies (react react-dom react-router express) installed. Also install any middleware you want to use through the components.
I do apologize for the number of dependencies. Its propbably nothing that you aren't already using though.
Then, follow one of the examples above to set up your server config. When done, feed the config to createServer
and the output of createServer
into serve
.
Before unleashing node
on your server file, keep in mind that you need Babel to transpile the JSX. I suggest using babel-core/register
to accomplish this if you do not have transpiling enabled for your server-side code. Like this:
// server.babel.js
require('babel-core/register')
require('server.js') // Your server file
Then run that file with Node.
When run, React-response will output the URL where you can see your app in the console. By default that would be http://localhost:3000
.
Future plans
This is but the very first release of React-response! Plans for the future include:
- Template engines like Jade and EJS
- Option to use something else than Express
- Add more
Response
components for the following needs:- Without a React Router dependency
- Redux
- Redux React Router
- Production stability and more tests
- Integrated reverse proxy for external APIs
- Maybe a Relay server?
- Your suggestions!
- Other cool features
Components
ReactServer
- Props:
- host
- Type: string
- Default: '0.0.0.0'
- port
- Type: integer
- Default: '3000'
- host
- The wrapper component for React-response. This component instantiates
http
andExpress
.
- Props:
Route
- Props:
- path
- Type: string,
- Default: '/'
- method
- Type: string,
- Default: 'get'
- path
- This component establishes a route context for the application handler and middleware. If
get
requests to the/
route is all you need to serve, you can skip theRoute
component. Everything mounted as children for this component will be served under the specified path.
- Props:
Template
- Props:
- component
- Type: React component
- Default: none
- component
- The Template component specifies the HTML template component you want to use for the route. This component will receive props from
Response
's render function; onlycomponent
by default. For now the only option is to use a React component, but I fully intend to make use of Express' native template engine capabilities to enable Jade, EJS and the rest. Stay tuned for future updates!
- Props:
Response
- Props:
- routes
- Type: React Router route configuration
- Default: none
- routes
- Children:
- responseHandler
- Type: function
- Arguments:
renderProps
,req
,res
- responseHandler
- The Response component creates a route handler for rendering your app. The handler will hande the route specified by the
Route
component, orget
on/
by default. Without specifying a custom render function as a child toResponse
, React-response will simply render your React Router routes. In this case the props applied to your template is onlycomponent
, which is your app rendered to a string.
- Props:
Middleware
- Props:
- use
- Type function (Express middleware)
- Default: dummy middleware
- use
- Directly applies the specified middleware into Express. Using this generic Middleware component you can accomplish many features that React-response does not currently cater to.
By inspecting the source code you might find out that the components can take more props than documented. These exist mainly for testing and decoupling purposes. Usage of undocumented props is not supported, but I am not your mother.
Test and build
I have a very simple React project set up in
/test/consumer
that demonstrates how to use React-response.cd
into there and runnode ./consumerenv.js
to run the test app. Unit tests are located in/test
and can be run withgulp test
. This project uses Tape and Sinon for testing.To build the app simply run
gulp build
.- Props: