Declarative client-side inclusion for the Web, using Custom Elements
Clone or download
gustafnk Revert "Merge pull request #58 from gustafnk/update-polyfill"
This reverts commit cfe3996, reversing
changes made to 9632c7d.
Latest commit e9088c5 Dec 17, 2018
Type Name Latest commit message Commit time
Failed to load latest commit information.
__tests__ Clean up Dec 16, 2018
static Revert "Merge pull request #58 from gustafnk/update-polyfill" Dec 17, 2018
.gitignore Update .gitignore Dec 17, 2018
.jshintrc Add support for onSuccess callback May 7, 2016
.travis.yml Travis: drop a defunct setting Dec 16, 2018 More documentation work May 24, 2018 README: Edit grammar, fix Markdown Dec 16, 2018
bower.json Bump version to 2.1.0 Jun 27, 2018
h-include.js Bump version to 2.1.0 Jun 27, 2018
package.json Clean up Dec 16, 2018


Declarative client-side transclusion. Perfect for Microfrontend architectures, in combination with server-side transclusion technologies like Edge-Side Includes.

Based on hinclude.js by @mnot.

Breaking change in version 2.0: changed configuration mechanism to use JavaScript instead of meta tag.


Include a document

<h-include src="/other/document/here.html"></h-include>

Include a document and extract a fragment

<h-include src="..." fragment=".container"></h-include>

Enable XMLHttpRequest.withCredentials

<h-include src="..." with-credentials></h-include>

Refresh an h-include element


Rendering Mode

By default, each include is fetched in the background and the page is updated only when they all are available.

This is bounded by a timeout, by default 2500 ms. After the timeout, h-include will show what it has and keep on listening for the remaining responses.

However, it's also possible to have h-includes become visible as they're available, see the configuration section below. While this shows the included content quicker, it may be less visually smooth.

Other features

  • Media query support
  • Easy to inherit to create lazy loaded includes
  • Changing the @src attribute works as expected and includes a new resource

See the demo page for more documentation and examples.


Set buffered include timeout (default is 2500 ms):

HIncludeConfig = { timeout: 10000 };

Set include mode to async (default is buffered):

HIncludeConfig = { mode: 'async' };

Throw if caught in an infinite include loop, to avoid the Droste effect:

HIncludeConfig = { checkRecursion: true };


Install using npm:

$ npm install h-include

Install using bower:

$ bower install h-include


h-include provides a custom element <h-include>. This means that you have to use a polyfill for enabling W3C Custom Elements for browsers not supporting it.

We recommend using document-register-element (3KB) as the polyfill for W3C Custom Elements.

Overridable function on the custom element

Function Arguments Default behavior Example override use case
createContainer request Creates a DOM container from the request Loop through links to resources and replace relative URLs with absolute URLs
extractFragment container, fragment, request Queries the container with a fragment selector. The default fragment selector is 'body'. Improved error handling if fragment doesn't match
replaceContent fragmentElement Replaces the innerHTML of the element with the innerHTML of the fragmentElement DOM diffing
onEnd request Add status information to the @class attribute of the h-include element

Override one or many functions by inheriting from the custom element, like this:

var proto = Object.create(HIncludeElement.prototype);

proto.onEnd = function(req){
  HIncludeElement.prototype.onEnd.apply(this, arguments); // call super

  // your code here

document.registerElement('h-include-improved', {
  prototype: proto,

Lazy loading example

This example uses the Intersection Observer API in a load event handler on window, to lazy-load <h-include-lazy> element content.

window.addEventListener('load', function() {
  var elements = document.getElementsByTagName('h-include-lazy');
  var config = {
    rootMargin: '400px 0px',
    threshold: 0.01 // 1% of the target is visible

  var observer = new IntersectionObserver(onIntersection, config);
  [], element => {

  function onIntersection(entries) {
    entries.forEach(entry => {
      if (entry.intersectionRatio > 0) {

var proto = Object.create(HIncludeElement.prototype);

proto.attachedCallback = function(){}

document.registerElement('h-include-lazy', {
  prototype: proto,

Media query support

It's possible to use media queries to have different fragments for different devices:

<h-include media="screen and (max-width: 600px)" src="small.html"></h-include>
<h-include media="screen and (min-width: 601px)" src="large.html"></h-include>


Add navigate attribute to let link navigation events be captured by h-include element itself, which changes the src attribute and triggers a refresh.

If navigate attribute is used, use target="_top" to let link inside h-include behave as a normal link.

Error Handling

If fetching the included URL results in a 404 Not Found status code, the class of the include element will be changed to include_404. Likewise, a 500 Server Error status code will result in the include element’s class being changed to include_500.

Browser support

All modern browsers and IE down to IE10 are supported. If you find something quirky, please file an issue.


Browsers with HTTP/2 are using HTTP/2 for xhr requests as well. So, if both the server and the current browser supports HTTP/2, all requests made with h-include will go through the same TCP connection, given that they have the same origin.


Please see the FAQ for some frequently asked questions.