pagehaus

Learn HTML · Lesson 6 of 8 · about 15 minutes

One header and footer for every page

In lesson 4 you copied the menu into every page. In this lesson you'll write it once, in a file of its own, and have it appear on every page. Change it there, and every page changes.

Includes: one line instead of a copy

HTML itself has no way to put one file inside another. Web servers do, and have since the 1990s: it's called Server Side Includes, or SSI. You write a line like this where the menu should go:

<!--#include virtual="/includes/menu.html" -->

When someone visits the page, the web server swaps that line for the contents of includes/menu.html before sending the page. The visitor gets one complete page, the same as if you'd copied the menu in by hand. So do search engines and screen readers.

The line is written like an HTML comment, <!-- -->, on purpose: anything that doesn't understand includes just ignores it. That includes your browser when you open the file from your computer, which is why you'll need a web server to see includes work. More on that below.

Put the shared parts in their own files

In your my-site folder, make a folder called includes, and in it a file called menu.html with just the menu, nothing else: no <!DOCTYPE>, no <head>, no <body>.

<nav>
  <a href="/index.html">Home</a>
  <a href="/recipes.html">Recipes</a>
</nav>

Do the same for a footer, in includes/footer.html:

<footer>
  <p>Baked and written by Sam Rivera ·
  <a href="mailto:sam@example.com">sam@example.com</a></p>
</footer>

Your site now looks like this:

my-site/
  index.html
  recipes.html
  style.css
  images/
    bread.jpg
  includes/
    menu.html
    footer.html
  recipes/
    sourdough.html

Why the menu's links start with /

The menu used to say href="recipes.html". That's relative to the page it's on, and the same menu is now on pages in different folders. On recipes/sourdough.html, recipes.html would mean recipes/recipes.html, which doesn't exist.

A leading / means "start from the top of the website", so /recipes.html is the same file from any page, in any folder. Lesson 4 said to avoid these, because they don't work when you open files from your computer. Includes need a web server anyway, and on a web server, addresses starting with / work perfectly. So from now on, use them in anything shared: the menu, the footer, and the stylesheet link (href="/style.css").

The include line itself uses virtual="/includes/menu.html" for the same reason: it's the same line on every page, wherever the page is.

Use them in every page

Now replace the <nav> in each page with the include line, and add the footer's at the end. recipes.html becomes:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Recipes · Sam Rivera</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <!--#include virtual="/includes/menu.html" -->

  <h1>Recipes</h1>
  <p>The things I bake most, written down so I stop forgetting them.</p>
  <ul>
    <li><a href="recipes/sourdough.html">Sourdough bread</a></li>
    <li>Cinnamon buns</li>
  </ul>

  <!--#include virtual="/includes/footer.html" -->
</body>
</html>

Do the same in index.html and recipes/sourdough.html: exactly the same two lines, even in the page inside the recipes folder. While you're there, change each page's stylesheet link to /style.css, so every page can use the same one.

Links that aren't in shared files, like the one to recipes/sourdough.html above, can stay as they are. Either kind works on a web server.

See it working

Open a page straight from your folder and the menu and footer are missing: your browser read the include lines as comments. To see the finished pages, the files need to come from a web server that does includes. Two ways:

Includes are an old, widely supported feature of web servers, but not every host turns them on. Apache-based hosts often do (sometimes only for files ending in .shtml); GitHub Pages, Netlify and most "static" hosts don't.

Going further: the head too

The top of every page, from <!DOCTYPE html> to the menu, is the same too, except for the title. You can include all of it, and still give each page its own title, by setting a variable first. Here's includes/head.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title><!--#echo var="title" --> · Sam Rivera</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <nav>
    <a href="/index.html">Home</a>
    <a href="/recipes.html">Recipes</a>
  </nav>

<!--#echo var="title" --> is replaced by the value of title. Each page sets it, then includes the file. A whole page is now just its own content:

<!--#set var="title" value="Recipes" -->
<!--#include virtual="/includes/head.html" -->

  <h1>Recipes</h1>
  <p>The things I bake most, written down so I stop forgetting them.</p>

<!--#include virtual="/includes/footer.html" -->

The footer file would then end with </body> and </html>, to close what the head file opened. It's optional, and a matter of taste: some people like seeing a full page in every file. But it means a new line in the <head> (a font, a favicon) is added in one place.

A year that updates itself

Includes can do a little more than include. This footer line always shows the current year, so you never have to update it in January:

<!--#config timefmt="%Y" -->
<p>© <!--#echo var="DATE_LOCAL" --> Sam Rivera</p>

More of these, like "last updated" dates, are in the Server Side Includes reference.

If it doesn't work

Check your work

Next, you'll add a contact form, so visitors can write to you without leaving your site.