npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-router-template

v3.2.0

Published

Provides a universal solution to rendering a react-router hierarchy on both the server and the client.

Downloads

37

Readme

react-router-template

NPM version Build Status Dependency Status

Quickly render universal react-router components using a simple API.

// Server
import template from 'react-router-template';

const render = template({
	routes,
	wrapHtml: html => `<!DOCTYPE HTML>${html}`,
	wrapComponent: component => (
		<Provider store={store}>
			{component}
		</Provider>
	)
});

app.get('*', (req, res) => {
	render(req).then(html => res.end(html));
});
// Browser
import template from 'react-router-template';

const render = template({
	routes,
	wrapComponent: component => (
		<Provider store={store}>
			{component}
		</Provider>
	)
});

render(document.getElementById('container'))
	.then(() => console.log('rendering complete'));

overview

react-router-template is a universal solution to rendering a react-router hierarchy on both the server and the browser.

Simply provide a template on how to render components with the option to alter the output as you like, then use the returned render function to either output a string (on the server) or mount on the DOM (in the browser). Common alterations to the output are wrapping it in a <Provider> or prepending a <!DOCTYPE>.

  • ignore complexities of universal rendering
  • use async routes on the browser with async rendering
  • juggle the output in any way you need.

Server usage

Server-side templates are defined with the following options:

  • routes - the <Route> hierarchy required for react-router to match against and render. Supports async routes
  • createElement - optional function passed to the <RouterContext>
  • wrapComponent - optional function that is passed the resulting <RouterContext>, allowing you to wrap / compose it in Providers (e.g. react-redux's <Provider>) or other additional React components
  • wrapHtml - optional function that allows you to decorate the resultant HTML string

Both wrapComponent and wrapHtml are evaluated as Promises, and thus can be either synchronous or asynchronous.

import template from 'react-router-template';
import { Route } from 'react-router';
import { Provider } from 'react-redux';
import { App, Index } from './components'; // or wherever you place them

const routes = (
	<Route path="/" component={App}>
		<Route path="/index" component={Index} />
	</Route>
);

// most basic use case
const render = template({ routes });

// using more options
const anotherRender = template({
	routes,
	wrapHtml: html => `<!DOCTYPE HTML>${html}`,
	wrapComponent: component => (
		<Provider store={store}>
			{component}
		</Provider>
	)
});

The output is a render() function that returns a Promise resolving into a string:

// assuming an express route
app.get('*', (req, res) => {
	render(req).then(html => res.end(html));
});

In the example above, the req object is passed in directly, but:

  • any object with the originalUrl property is sufficient.
  • a string representing the path to render is also sufficient.
// both of these will work
render('/some-path');
render({ originalUrl: '/some-path'});

not founds and redirects

When the path specified cannot be found or redirects, the render function will throw an Error with the following properties:

  • .status or statusCode will be a number, 404 for not found, 302 for redirects.
  • For redirects, .location or .url will also be present to allow the developer to take the appropriate action
app.get('*', (req, res) => {
	render(req)
		.then(html => res.end(html))
		.catch(err => {
			const { status, location } = err;
			if (status === 404) {
				// handle not found
			} else if (status === 302) {
				res.redirect(location);
			} else {
				// other error handling
			}
		});
});

Browser usage

Browser templates are defined with the following options:

  • routes - the <Route> hierarchy required for react-router to match against and render. Supports async routes
  • createElement - optional function passed to the <RouterContext>
  • wrapComponent - optional function that is passed the resulting <RouterContext>, allowing you to wrap / compose it in Providers (e.g. react-redux's <Provider>) or other additional React components

wrapComponent is evaluated as a Promise and thus can be either synchronous or asynchronous.

import template from 'react-router-template';
import { Route } from 'react-router';
import { Provider } from 'react-redux';
import { App, Index } from './components'; // or wherever you place them

// most basic use case
const render = template({
	routes
});

// using more options
const render = template({
	routes,
	wrapComponent: component => (
		<Provider store={store}>
			{component}
		</Provider>
	)
});

The resulting render() is a promise-returning function that takes up to 2 arguments, rather than just one. Instead of a path (or an object containing originalUrl):

  • the first argument can be a history object (or object containing a history property).
  • the second argument, if present, should be the DOM element to mount the React component to.
// some client JS entry point
import { browserHistory } from 'react-router';

const target = document.getElementById('container');

render(browserHistory, target)
	.then(() => console.log('rendering complete'));

If the history object is not specified (i.e. only one argument, the target, is supplied), then it is assumed that react-router's browserHistory will be used.

// both of these will work
render({ history: browserHistory}, target);
render(target);

This reflects the nature of client-side rendering - the path is usually determined from the browser context rather than being supplied, and the second argument matches React's own render signature.

NOTE: There is no wrapHtml option, because on the browser React is primarily concerned with mounting the component onto an existing DOM, not producing any HTML string output.

error handling

There is no special API for errors on the browser, apart from the fact that any error is caught as a Promise rejection.

Redirects are handled automatically on the browser for you via the supplied history object.

Any errors created are no different than if you weren't using react-router-template, but just React's native .render() method.

render(browserHistory, target)
	.catch(err => console.error(err));

note regarding client-side bundlers like webpack

Bundlers are expected to use the entrypoint specified by browser in this module's package.json as per the specifications outlined here