New feature: Isomorphic rendering—server-side html produced using javascript templates

PoP now provides isomorphic rendering capabilities: it allows to render HTML both in the server-side and in the front-end, and the code used for both is the same: precompiled Handlebars javascript templates, rendered in the front-end using the Handlebars runtime library, and in the back-end using LightnCandy.

This feature allows to render HTML straight from the server when first loading the website, which produces a very quick initial response, yet render all HTML code in the frontend through javascript, in a Single-Page Application fashion, from the 2nd page-load onwards.

What are the advantages of isomorphism?

There are quite a few, such as:

SEO: all search engines can parse the site’s HTML (Google can parse websites built with Javascript, but others, such as Bing, still can’t).

Integration with technologies and systems: server-side HTML rendering is needed for implementing new technologies such as AMP and microformats, and for broad system interoperability.

Speed: the first page is loaded much faster, since it doesn’t need to wait to load javascript templates to render it. Starting on the 2nd page onwards, javascript takes over, rendering faster and making the website dynamic.

Re-usability: developers implement the code only once and use it everywhere: not just for producing HTML in the front and back-end, but even for generating transactional emails, embeddable widgets, and others.

Is it 100% fully isomorphic?

Ok, you got me. The amount of code that is reusable is around 95%, which corresponds to all the views produced using Handlebars templates. The remaining 5% corresponds to Handlebars helpers, that is javascript functions executed when producing the HTML code.

Javascript helper functions must be coded also in PHP for generating the HTML in the server-side, using LightnCandy. However, the syntaxes of javascript and PHP are so similar that this is such a trivial matter. Check out the following example:

Javascript helper function withModule:

Handlebars.registerHelper('withModule', function(context, module, options) {

	if (typeof context == 'undefined' || typeof context[M.JS_SETTINGSIDS] == 'undefined' || typeof context[M.JS_SETTINGSIDS][module] == 'undefined') {

		return;
	}

	// Get the module settings id from the configuration
	var moduleSettingsId = context[M.JS_SETTINGSIDS][module];//context['settings-ids'][module];

	if (typeof context[M.JS_MODULES] == 'undefined' || typeof context[M.JS_MODULES][moduleSettingsId] == 'undefined') {

		return;
	}

	// Go down to the module
	context = context[M.JS_MODULES][moduleSettingsId];

	// Expand the JS Keys
	popManager.expandJSKeys(context);

	// Read all hash options, and add them to the Context
	jQuery.extend(context, options.hash);

	return options.fn(context);
});

PHP helper function withModule:

function withModule($context, $module, $options) { 

	if (!$context || !isset($context[GD_JS_SETTINGSIDS]) || !isset($context[GD_JS_SETTINGSIDS][$module])) {

		return;
	}

	// Get the module settings id from the configuration
	$moduleSettingsId = $context[GD_JS_SETTINGSIDS][$module];

	if (!isset($context[GD_JS_MODULES]) || !isset($context[GD_JS_MODULES][$moduleSettingsId])) {

		return;
	}

	// Go down to the module
	$context = $context[GD_JS_MODULES][$moduleSettingsId];

	// Expand the JS Keys
	$popManager = PoP_ServerSide_Libraries_Factory::get_popmanager_instance();
	$popManager->expandJSKeys($context);

	// Read all hash options, and add them to the Context
	$context = array_merge(
		$context,
		$options['hash'] ?? array()
	);

	return $options['fn']($context);
}

From this function, it can be observed that migrating a function from Javascript to PHP involves a series of simple, well-defined steps:

  • Add $ to all variable names
  • type array[key] == 'undefined' => isset(array[key])
  • Use global $variable (or, in this case, access an instance through a Factory) to access some globally defined object, such as popManager in the javascript function.
  • Access functions using -> instead of .
  • jQuery.extend() => array_merge()

And voilà, in under 2 minutes, rather effortlessly, the function is ported from Javascript to PHP.

What comes next?

Having isomorphic rendering capabilities enables us to implement the following features in PoP:

  • Issue #53: AMP — Accelerated Mobile Pages Project
  • Issue #51: Microformats to integrate with the IndieWeb
  • Issue #46: Email digests
  • Issue #1: Create standalone PoP plug-ins, to provide functionalities from PoP to install on any WordPress website

As always, enjoy!

Welcome to the PoP framework!
Break the information monopoly

the PoP framework is open source software which aims to decentralize the content flow and break the information monopoly from large internet corporations. Read more.


Sign up to our newsletter: