Taco Steemers

A personal blog.

How to include HTML documents inside HTML documents

There are two good ways to include our static HTML documents inside the main HTML documents that make up our website. One is to include them server-side, before the server sends the main HTML document to the web browser. The other is client-side loading where we have Javascript on the client (the web browser) request information from a server, format that as HTML and append that to the main HTML document inside the web browser. It is the best way to load dynamic content, such as a feed that receives updates. Mozilla provides a resource that explains why you would want to use this method to load dynamic data. However, the topic of my article today is including static HTML documents, and for that purpose I personally find client-side loading to be too involved. For example, it requires putting some thought in to error handling, and the order in which information and styling becomes visible to people during the page loading phase. To keep this article short I will skip client-side loading. I do want to mention the security implications related to loading other people's documents into your pages. To keep our users safe we have to assume that not all security problems can be solved by restrictions inside web browsers, and keep an eye on the security implications of using documents and scripts that we did not make ourselves.

There is a third way that we will discuss first. An iframe can be used to load an HTML document inside another document.


From talking to colleagues I know that they don't like iframes. Perhaps they have one of the many criticisms listed here . One of the biggest downsides of iframes, also listed on that page, is that screen readers have difficulties to explain them to the user.

In my opinion the iframe has one purpose that it serves well. It allows one to load an entirely separate page into another page. This can be handy for all sorts of uses such as manuals, blogs with comments, or separated functionality such as a widget that contains a video or a comic panel. In case of classic, book-inspired manuals the iframe can be used to create a navigation panel on one side of the page and show the user the page or manual they asked for on the other side of the page. Blogs with external commenting systems can use them to load the commenting functionality without letting the external scripts have access to the other contents of the blog. For external video and picture files it has the same advantages. If these contents from external documents were not restricted to the size of their iframe they might be able to show a full-page advertisement.

Given all the criticisms of iframes we saw on that Wikipedia page earlier, we really should avoid using them. At the time of writing I am unfortunately still using them in two locations on this website.

One is a page where I have some easy to use timers . I use them as a pomodoro timer and a break timer. To avoid having to add the same HTML several times I load these timers with an iframe. There is an important principle in computer programming: don't repeat yourself . The timers need HTML tags, styling and javascript code to be able to function as intended. If I were to paste several copies of everything they need on the same page I would be violating this DRY principle. The result would be that a single mistake would have to be fixed in several places. As I wrote about before, we don't want to set ourselves up for mistakes .

Another place I use an iframe is a page where I embed a board that I use when I want to track the progress of several smaller tasks I have to accomplish during my work day. My justification for using it here is that this board is a separate application maintained in a separate codebase . If I want to use an updated version on this website I copy the files over and overwrite the ones that were there. The naive way to avoid using an iframe for this use case is copying the actual HTML in to the Markdown document that my static blog generator uses to generate the page that ends up on my website. The reason we should avoid doing that is that it would again lead to repetition and an increased risk of mistakes.

Server-side inclusion

I am going to stop using iframes for these use cases. Instead, I am going to use the include statement of the blog generator's template language to include these HTML documents in to the main HTML document on the server, before the server sends the main document to the client. I will remove the head and body tags from the timer document. That way we can include the timer document without ending up with several head and body tags, as there should be only one of those if the document is not inside an iframe. Luckily documents without those tags are considered valid since at least 2014 . As a result of that we will still be able to use the vertical board and the timers as stand-alone tools even when omitting these tags.

The downside for the timers example is that the payload increases in size. In the iframe situation the web browser would do only one extra request to the server to get the embedded HTML document that contains the timer. The web browser is smart enough to realise that it doesn't make sense to make another request for the other two timers; the response would be the same. In the new situation the three copies of the timer will actually have to be sent to the client in duplicate. All three will be sent as part of the main HTML document that contains them. In this case it is entirely acceptable because the timer HTML is small.

Currently, the timer Javascript and CSS are stored inside the timer HTML. Apart from some laziness, a valid reason to keep it that way is that by not using separate files for the Javascript and the CSS we avoid creating two extra HTTPS requests to the server. Keeping the JS and CSS separate from the HTML is what one should usually do in more standard situations . When the timer documents are not in iframes anymore I will be move the JS and CSS to separate files. In this particular situation it will come at the cost of two more HTTPS requests to the server; one for the JS file and one for the CSS file. If anyone is still with me at this point: we will be making one more request than we did in the original situation. At the scale of a website like this the extra load on the server will not be noticed.

If we insist on using an iframe

If we insist on using an iframe, let's use them well! Iframes have some cross-browser problems that I have run in to myself.

One is that the difference in screen size between a computer screen and a smartphone screen can be large. We still need to get the document inside the iframe to be displayed properly on both screen sizes, without pushing away the content on the main document. The difference in screen sizes could in the past easily be accounted for with CSS media queries in combination with device-related CSS properties like max-device-height . Unfortunately these device-related properties are deprecated. The word 'deprecated' means that it has fallen out of use and is no longer supported by the browser developers. The browser on my phone doesn't support it any more. That is how I found out that the property is deprecated even though testing on my computer showed that it worked as expected.

In my case I want the contents to be shown correctly on different sizes of screens, but I also want to show a message that explains the vertical board application does not work on small screens. To get a similar effect today, without using the device-related CSS properties, we have to take three steps. - we use the regular max-height css property on the media queries inside the iframe to hide the application on the small screen and show the message instead.

@media only screen and (max-height: 800px) {
    #application {
        display: none;
    #notmobilefriendly {
        display: block;
- we set width: 100% on the body inside the iframe
body {
    width: 100%;
- on the iframe tag in the main document we add CSS to set a size based on how big the browser window is, like this:
    height: calc(100vh - 80px);
    width: 100vw;
Here I have subtracted the height of the header on the main document from the height of the window (vh, which stands for view height) to get the correct height for the iframe. The browser will now calculate the correct height and width for us.

Another problem is that the Safari webbrowser, used on Apple devices, does not handle iframe content size well. We will skip the details and go right to the solution.

  • We place the iframe inside a container that has some size of its own, a padded div for example:

<div style="padding: 1px;"><iframe class="timer" src="timer.html"></iframe></div>

  • for this situation we also set width: 100% on the body inside the iframe
    body {
        width: 100%;

Now the Safari webbrowser will also give the iframe the requested dimensions.

Web browser behaviour is a moving target

To get non-standard things like dynamically-sized iframes to work predictably across all web browsers is a difficult task. What makes it even more difficult is that the goal of having your document look the same in all browsers is a moving target; browsers receive updates and people on different continents tend to use different browsers. Whereas in Europe we mainly use Firefox, Chrome, Safari and Edge we find other browsers in Asia. At the time of writing, this statcounter page about browser market share in China shows that the UC Browser and QQ Browser have a combined marketshare of 22.6% there.

Keeping the 'why' in mind

I think the best way to look at web pages is that they are a way to provide information to people. We can't expect the pages to look the same in all web browsers. We must make sure that the information we want to provide to people is readable in all browsers. For that reason I find it best to keep our designs simple. Our design can be hip, or elegant, or show our personality and that of our company, but the information must be readable at all times.