What are HTML Imports and How Do They Work?


Have you ever noticed that including another HTML page inside another is like a foreign concept? It’s something that should be easy to do but you don’t see it often. It’s not impossible but tricky. However, HTML imports makes placing documents into HTML pages such as other HTML pages, CSS files and JavaScript files a manageable task.

Introducing HTML Imports

HTML imports is an easy concept to grasp; it is a way to insert other HTML pages into an HTML page. You’d think that this is not that a big deal but it is; you couldn’t do that with ease before.

The funny thing is, HTML is a basic file yet it did in fact require the most effort to work with sometimes. Even PHP files had the capability of includes, why not HTML? Thanks to web components we can now include HTML documents in another HTML documents; you can also load other CSS and JavaScript files with the tag as well. (Life just go that much more awesome.)

Work Around Tricks

Some previously common options included using an iFrame; which are load-heavy elements within the HTML page inside a separate window on the page. It’s kind of out of context and they weren’t the best when it comes to manipulation and display. iFrames can be fascinatingly frustrating to work with. The second most popular option was AJAX, where you preloaded the page through JavaScript and embedded the content. This is a very inconvenient and tedious way to embed content actually.

Getting started

Basic Syntax

Before we go over the examples, let’s briefly go over the syntax of loading a file through the HTML import tag. Imports are a new type of link tag so it should be easy to understand with the following line of code:

<link rel="import" href="/folder/page.html">

They are placed in the header like you’d like a JavaScript or a CSS file.

Basic Example

In order for the imports to work, you need to have all the pages in question set up on a local server. Now, let’s get started with index.html. Here is a basic HTML page with a basic import:

<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <title>What are HTML imports and how do they work</title>
    <link rel="import" href="/intro.html">
    <h1>Hello from Designmodo</h1>

As you can see, I wasn’t lying about the simplicity; it’s just like calling a stylesheet or a JavaScript file.

Inside intro.html

So what’s inside this imported HTML page?

<div id="intro-dm">
  <h2>We're an awesome blog about web design</h2>
  <p>Designmodo is a great resource of informative material for designers and web developers. We are makers of highly-rated User Interface Packs, you can get acquainted with Designmodo shop here, and you can download a couple of other ui packs for free.</p>

It’s just a div with some text in it. No need for body or head or any of that. (Talk about simple!)

Appending Imported HTML

In order for imported HTML to show up we do need to use some JavaScript. The code is below. Place it in your original HTML file — for us index.html — and place it in the markup where you’d like the imported file to appear. In this example it’s after ‘Hello from Designmodo.’

In the snippet, we query the content and add it into a variable. Next, we just append what the variable hold into the HTML.

<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <title>What are HTML imports and how do they work</title>
    <link rel="import" href="/intro.html">
    <h1>Hello from Designmodo</h1>

      var link = document.querySelector('link[rel=import]');
      var content = link.import.querySelector('#intro-dm');


Taking It To the Next Level

Have you heard of the scoped attribute in CSS? The scoped attribute let you use a <style> tag inside of an element. The awesome part is that it only affects that element, not the rest of the document as long as the <style> tag is accompanied by “scoped.” Let’s see how the scoped attribute can help us with the imported HTML file.

Add an h1 into the imported document so we can see the difference between the imported HTML’s CSS and index.html. Second, add style to the elements within the imported HTML document. As you can see <style scoped> is literally within the HTML markup not in the head. Thanks to scoped, only the h1 within the imported markup should be affected.

&lt;div id=&quot;intro-dm&quot;&gt;
  &lt;style scoped&gt;
    h1{ color: red; }
    p{ color: blue; }
  &lt;h1&gt;About us&lt;h1&gt;
  &lt;h2&gt;We're an awesome blog about web design&lt;/h2&gt;
  &lt;p&gt;Designmodo is a great resource of informative material for designers and web developers. We are makers of highly-rated User Interface Packs, you can get acquainted with Designmodo shop here, and you can download a couple of other ui packs for free.&lt;/p&gt;

If you want to read up some more on the scoped attribute checkout what W3C has to say.

It’s Kind of Like Bootstrap

The way Bootstrap works is you have a bunch of individual files such as bootstrap.css, boostrap.js, and so on. In order to use plugins, you use jQuery; Bootstrap provides markup examples for you. It’s very flexible and easy to manage. The thinking behind Bootstrap was that you would only use the files your project needs. Now, most people download all of it at once, and that’s okay too. The thinking behind HTML imports is similar where you only load files that you need when you need them. This type of logic is becoming more popular as it makes load times faster and organization easier.


Thanks to HTML imports, you can bundle other HTML files as well as CSS and JavaScript files into single resources. This is actually a pretty big deal. Embedding HTML files inside one another was not that easy to do, until HTML imports came around. We are now able to create reusable content that others can bring into their projects thanks to simple line of code. That is powerful — and that is a big deal!


  1. Viktor May 7, 12:20 pm

    Hi, do you have some experiences about how fast is content changing on changed source html? If i do the same with iframes, browser caching is a pain on often changed source content.

  2. markeb23 May 7, 7:48 pm

    Currently this is only an experimental feature. Native imports are only available in Chrome Canary v36…

  3. Jacob May 8, 1:00 am

    So, you complain about using AJAX with this sentence: “This is horrible because why should you need JavaScript to load an HTML page; it doesn’t make sense.”

    The you go on to describe a technique that requires both an HTML tag AND javascript? This article is not good as it is attempting to steer people toward an unsupported feature that is actually more complicated than any of the existing solutions.

    • Paula Borowska May 8, 5:35 pm

      You and Rick are both right. I totally misspoke! I meant to imply that Ajax is complicated and makes life difficult where as using HTML imports is easier than that.

  4. Rick May 8, 1:36 am

    When referring to AJAX you say how it’s such a horrible idea to require JavaScript to load an HTML page and it doesn’t make sense… Yet this solution also requires JavaScript.

    Or did I miss something?

  5. Daniele May 9, 11:07 am

    Imho, a better solution is the esi:include tag. But you need a server that supports ESI.

    This method render an HTML page without need of javascript to compose it and it’s complete transparent for the client. Every single part of the page has its own HTTP cache rules that are respected.

  6. Ahmad Alfy May 13, 11:39 am

    I don’t think HTML Imports have any future, they are trying to solve a problem that doesn’t exist on production. IMHO it will only be useful when you are wire framing.

    I started using Jade in the last 2 projects I am working on. I deliver HTML files to the server-side-stuff guys and they do the integration. Using Grunt.js and Jade’s “include” helped me speed up the process of modifying the parts that are repeated across the whole site like the header,footer … etc

  7. McAsh May 13, 11:51 am

    You can also use The Kit Language. Simply include another file like this

    It is very easy and straightforward to use and it’s even possible to use variables with Kit.


  8. Danny May 13, 6:50 pm

    How is this any different than using .shtml and the syntax? Is there any benefit over .shtml?

    More about it here:

  9. e11world May 13, 10:18 pm

    Why not just use php includes? It’s the same in so many ways.

  10. z May 14, 6:41 am

    What’s the browser compatibility?

  11. Steve May 28, 5:46 pm

    Tried your exact code running pages from my localhost through the latest version of chrome and all I get is an error of “Uncaught TypeError: Cannot read property ‘querySelector’ of undefined”

    Any ideas?

    • Jessica Jan 22, 8:51 am

      I am having the exact same problem, but in localhost and online. I have spent the last 4 hours trying to figure out what happened. This is not my first time with imports, but this particular code gives me that same error on “t = remoteDocument.querySelector(“#” + templateId).content;”

      function getTemplate(templateId, remoteFile) {
      console.log(“getting ” + templateId + ” from ” + remoteFile);
      // Return nothing, if no template was specified
      if (templateId == null || templateId == “”) {
      console.log(“Template not found.”);
      // If remoteFile is specified, import file before loading the template
      if (remoteFile != null && remoteFile != “”) {
      if (supportsImports()) {
      var link = document.createElement(‘link’);
      link.rel = ‘import’;
      link.href = remoteFile;
      link.onerror = function(e) {
      console.log(“HTML5 import failed.”)
      var remoteDocument = document.querySelector(‘link[rel=”import”]’).import;
      } else {
      console.log(“HTML5 imports not supported.”);

      // Execute only if the “template” tag is supported
      if (supportsTemplate()) {
      var t;
      // Reference to the template’s content
      if (remoteFile != null && remoteFile != “”) { // If template is from a remote file…
      t = remoteDocument.querySelector(“#” + templateId).content;
      } else { // If template is not from a remote file
      t = document.querySelector(“#” + templateId).content;
      // Copy the actual contents of the template
      var clone = document.importNode(t, true);
      // Return the contents
      return clone;
      } else {
      console.log(“HTML5 ‘template’ tag not supported.”);

      Any ideas? Help would be greatly appreciated! I don’t know what to do anymore. :(

Leave a Reply

* Minimum length: 20 characters