Skip to content

Wrapping HTML responses with the shell and layout components #524

Open
@i-like-robots

Description

@i-like-robots

I'm opening this issue to discuss how we should provide a package capable of neatly integrating the shell and layout components into our existing applications in order to "wrap" their HTML output with the shared page bootstrapping, metadata, core branding, and UI.

As initially discussed in #98 "preset" packages like this can be used to prevent applications implementing lots of divergent custom code.

Our current approach

The v0.1.0 n-ui to Page Kit migration guide instructs developers to add a callback argument to Express's response.render() method in order to capture the HTML output so that it can be used for a second render step. This is not too dissimilar to the the existing Handlebars layout mechanism it replaces.

The approach is "intentionally hacky" in order to expose how the parts fits together and it is relatively simple. However, it means Express arguments need to be passed around in addition to configuration data for the shell and layout components which may be confusing and tricky to mentally parse.

// server/controllers/lib/page-kit-wrapper.js
const React = require('react');
const ReactDOM = require('react-dom/server');
const { Shell } = require('@financial-times/dotcom-ui-shell');
const { Layout } = require('@financial-times/dotcom-ui-layout');

module.exports = ({ response, next, shellProps, layoutProps }) => {
	return (error, html) => {
		if (error) {
			return next(error);
		}

		const document = React.createElement(
			Shell,
			{ ...shellProps },
			React.createElement(Layout, { ...layoutProps, contents: html })
		);

		response.send('<!DOCTYPE html>' + ReactDOM.renderToStaticMarkup(document));
	};
};

// server/controllers/page.js
const pageKitWrapper = require('./lib/page-kit-wrapper.js');

module.exports = (request, response, next) => {
	const templateData = {};
	
	const shellProps = {
		pageTitle: content.title,
	};

	const layoutProps = {
		navigationData: response.locals.navigation,
		headerOptions: { ...response.locals.anon }
	};

	const pageKitOptions = { request, response, next, shellProps, layoutProps };
	
	response.render('video.html', templateData, pageKitWrapper(pageKitOptions));
};

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions