Writing in pure HTML
Authors
Building Hugo Homepage Layout
Hugo builds websites by reading content files and layout templates. When starting without a theme, layouts must be created manually using HTML. The homepage layout file controls how content appears on the main page of the site.
Here we will see:
- how to write content in pure HTML
- how to get content from markdown files
- how to use site and page variables
Section 1: Writing Content in Pure HTML
Background
HTML is the foundational language for creating web pages. Every webpage you see is built using HTML tags that tell the browser how to display content. In Hugo, the homepage layout is created at layouts/_default/index.html. This file can contain any valid HTML, and Hugo will use it to generate the homepage. Even a simple HTML file with just a body tag will create a functional webpage, though it will be quite basic.
Exercises
This section covers creating HTML layouts from scratch and understanding how HTML tags structure web content.
| Concept | Description |
|---|---|
<body> |
Container for visible page content |
<h1>, <h2> |
Heading tags for titles |
<head> |
Container for page metadata |
<title> |
Text that appears in browser tab |
<footer> |
Section for bottom page content |
<p> |
Paragraph tag for text blocks |
<a href=""> |
Link tag for clickable URLs |
The simplest HTML file starts with a body tag. The body tag wraps all visible content that appears on the webpage. When you view the page in a browser, only what is inside the body tag will be displayed to visitors. This is where all text, images, links, and other visible elements belong.
Example: Create layouts/_default/index.html with a simple greeting
<body>
Hi,
</body>Exercise: Change the greeting from “Hi,” to “Your Name”.
Solution
<body>
Your Name
</body>Exercise: Remove “Your Name” to see what happens with an empty body tag.
Solution
<body>
</body>Exercise: Add “Your Name” back into the body.
Solution
<body>
Your Name
</body>HTML tags provide structure and meaning to content. Heading tags like <h1> through <h6> create titles of different sizes, with <h1> being the largest and most important. These tags help both browsers and search engines understand the hierarchy and importance of content on the page. Using appropriate heading levels makes websites more accessible and easier to navigate.
Example: Change your name to a level 2 heading in the layouts/_default/index.html file.
<body>
<h2>Your Name</h2>
</body>Exercise: Change the heading from level 2 to level 1.
Solution
<body>
<h1>Your Name</h1>
</body>Exercise: Add a GitHub link below your name.
Solution
<body>
<h1>Your Name</h1>
<a href="https://github.com/username">GitHub</a>
</body>A complete HTML document requires more than just a body tag. The head section contains metadata about the page that does not appear in the visible content but is important for browsers and search engines. The title tag inside the head determines what text appears in the browser tab when someone visits your page. Without these additional tags, browsers will still display the page, but it will lack important information that affects how the page appears and functions.
Example: Add a tab title with the head and title tags in the layouts/_default/index.html file.
<head>
<title>Title</title>
</head>
<body>
your content
</body>Exercise: Change the tab title to “Your Name - Portfolio”.
Solution
<head>
<title>Your Name - Portfolio</title>
</head>
<body>
your content
</body>Exercise: Wrap the entire document in an <html> tag.
Solution
<html>
<head>
<title>Your Name - Portfolio</title>
</head>
<body>
your content
</body>
</html>The footer tag creates a section at the bottom of the page for information like copyright notices, contact links, or other supplementary content. While technically optional, footers are a standard part of professional websites and help visitors find important information. The paragraph tag wraps text content and adds appropriate spacing around it, making the page more readable.
Example: Add a footer with copyright information in the layouts/_default/index.html file.
<head>
<title>Title</title>
</head>
<body>
your content
</body>
<footer>
© 2025 All rights reserved
</footer>Exercise: Add your name before the copyright symbol.
Solution
<head>
<title>Title</title>
</head>
<body>
your content
</body>
<footer>
© 2025 Your Name All rights reserved
</footer>Exercise: Wrap the copyright line within a <p> tag.
Solution
<head>
<title>Title</title>
</head>
<body>
your content
</body>
<footer>
<p>© 2025 Your Name All rights reserved</p>
</footer>Section 2: Getting Content from _index.md
Background
Hugo separates content from presentation by storing written content in markdown files and using layout templates to display that content. The {{ .Content }} variable in a layout file tells Hugo where to insert the markdown content from content/_index.md. This separation allows you to update the website content without touching the HTML layout, making content management much easier. Hugo also provides built-in functions to transform and manipulate content in various ways.
Exercises
This section covers accessing markdown content and applying transformations to that content.
| Concept | Description |
|---|---|
{{ .Content }} |
Displays full markdown content |
{{ .Summary }} |
Displays content summary |
| lower |
Converts text to lowercase |
| upper |
Converts text to uppercase |
| title |
Converts text to title case |
| truncate |
Shortens text to specified length |
| countwords |
Counts number of words |
| len |
Counts number of characters |
The Content variable retrieves all the text from the corresponding markdown file and converts it to HTML. When Hugo processes the layout, it replaces {{ .Content }} with whatever is written in the markdown file. This makes it easy to update website text without editing HTML code directly.
Example: Use the content from the content/_index.md file in the layouts/_default/index.html layout.
Add to content/_index.md:
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.Add to layouts/_default/index.html:
<body>
{{ .Content }}
</body>Exercise: Use the Content variable multiple times to repeat the content.
Solution
<body>
{{ .Content }}
{{ .Content }}
</body>Exercise: Use Summary and Content together to show a preview followed by the full text.
Add to content/_index.md:
Solution
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.Add to layouts/_default/index.html:
Solution
<body>
{{ .Summary }}
{{ .Content }}
</body>Hugo includes functions to transform text case, which can be useful for styling purposes or ensuring consistency. The pipe symbol passes the content to a function that modifies it. These transformations happen when the page is generated, so the original markdown file remains unchanged.
Example: Convert all content to lowercase in the layouts/_default/index.html file.
Add to content/_index.md:
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.<body>
{{ .Content | lower }}
</body>Exercise: Convert all content to uppercase.
Add to content/_index.md:
Solution
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.<body>
{{ .Content | upper }}
</body>Exercise: Convert all content to title case.
Add to content/_index.md:
Solution
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.<body>
{{ .Content | title }}
</body>Content operations let you manipulate and measure text. The truncate function shortens long text to a specified word count, which is useful for creating preview snippets. The countwords and len functions provide information about content length, which can be helpful for displaying reading time estimates or enforcing content length requirements.
Example: Truncate the content to 100 words in the layouts/_default/index.html file.
Add to content/_index.md:
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.<body>
{{ .Content | truncate 100 }}
</body>Exercise: Truncate content to 500 words.
Add to content/_index.md:
Solution
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.<body>
{{ .Content | truncate 500 }}
</body>Exercise: Count the number of words in the content.
Add to content/_index.md:
Solution
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.<body>
{{ .Content | countwords }}
</body>Exercise: Count the number of characters in the content.
Add to content/_index.md:
Solution
+++
title = "Home"
+++
Welcome to my portfolio website. I am a web developer with experience in building modern, responsive websites.
I specialize in creating clean, user-friendly interfaces that help businesses reach their goals online.<body>
{{ .Content | len }}
</body>Section 3: Using Site and Page Variables
Background
Hugo provides access to configuration values and page metadata through variables. Site variables come from the hugo.toml configuration file and contain information that applies to the entire website, like the site title or author name. Page variables contain information specific to each page, like the page title or publication date. Using variables instead of hard-coding values makes websites easier to maintain because changes to the configuration file automatically update across all pages. Conditional statements allow you to display content only when certain variables exist, preventing errors when optional information is missing.
Exercises
This section covers accessing configuration values and page metadata through Hugo variables.
| Concept | Description |
|---|---|
{{ .Site.Title }} |
Site title from configuration |
{{ .Site.Params }} |
Custom parameters from config |
{{ .Title }} |
Current page title |
{{ .Description }} |
Current page description |
{{ .Date }} |
Current page publication date |
{{ if }} ... {{ else }} ... {{ end }} |
Conditional statement with alternative |
Site variables pull information from the Hugo configuration file. The Site.Title variable retrieves the title you set in hugo.toml, which typically represents your website name or brand. This approach means you only need to change the title in one place for it to update across all pages that use this variable.
Example: Add the site title from the hugo.toml file in the head title tag of the layouts/_default/index.html file.
<head>
<title>{{ .Site.Title }}</title>
</head>Exercise: Change the title in hugo.toml and verify the change appears on the page.
Solution
<head>
<title>{{ .Site.Title }}</title>
</head>Exercise: Combine site title with the word “Portfolio” to create a compound title.
Solution
<head>
<title>{{ .Site.Title }} - Portfolio</title>
</head>Custom parameters defined in the configuration file are accessed through Site.Params. These parameters can store any information you want to reuse across your site, such as author names, email addresses, or social media links. Adding custom parameters to the configuration file is done by creating a params section in hugo.toml.
Example: Add the author name in the footer from the configuration
Add to hugo.toml:
[params]
author = "Your Name"Add to layouts/_default/index.html:
<footer>
<p>© 2025 {{ .Site.Params.author }} All rights reserved</p>
</footer>Exercise: Add an email link in the footer using a configuration parameter.
Add to hugo.toml:
Solution
[params]
email = "your.email@example.com"Add to layouts/_default/index.html:
Solution
<footer>
<a href="mailto:{{ .Site.Params.email }}">{{ .Site.Params.email }}</a>
</footer>Exercise: Add a GitHub link in the footer using a configuration parameter.
Add to hugo.toml:
Solution
[params]
github = "https://github.com/username"Add to layouts/_default/index.html:
Solution
<footer>
<a href="{{ .Site.Params.github }}">GitHub</a>
</footer>Page variables contain information specific to each individual page rather than the entire site. The Title variable retrieves the title from the page front matter, the Description variable gets the page description, and the Date variable accesses the publication date. The Format function can transform dates into different display formats using Go date formatting syntax.
Example: Add the page title as a heading
Add to content/_index.md front matter:
+++
title = "Welcome to My Portfolio"
+++Add to layouts/_default/index.html:
<body>
<h1>{{ .Title }}</h1>
</body>Exercise: Add the page description below the title.
Add to content/_index.md front matter:
Solution
+++
title = "Welcome to My Portfolio"
description = "Showcasing my projects and skills in web development"
+++Add to layouts/_default/index.html:
Solution
<body>
<h1>{{ .Title }}</h1>
<p>{{ .Description }}</p>
</body>Exercise: Add the page publication date with formatted output.
Add to content/_index.md front matter:
Solution
+++
title = "Welcome to My Portfolio"
date = 2025-01-15
+++Add to layouts/_default/index.html:
Solution
<body>
<p>Published: {{ .Date.Format "January 2, 2006" }}</p>
</body>Conditional statements check whether a variable has a value before displaying content. This prevents errors or empty spaces when optional information is not provided. The if statement checks if a variable exists or has a value, and only the content between if and end will be displayed when the condition is true. The else statement provides alternative content to display when the condition is false.
Example: Show the author name only if it exists in the configuration
Add to hugo.toml:
[params]
author = "Your Name"Add to layouts/_default/index.html:
<footer>
{{ if .Site.Params.author }}
<p>© 2025 {{ .Site.Params.author }} All rights reserved</p>
{{ end }}
</footer>Exercise: Show the email link only if the email parameter exists.
Add to hugo.toml:
Solution
[params]
email = "your.email@example.com"Add to layouts/_default/index.html:
Solution
<footer>
{{ if .Site.Params.email }}
<a href="mailto:{{ .Site.Params.email }}">Contact</a>
{{ end }}
</footer>Exercise: Show different footer text depending on whether the author parameter exists.
Add to hugo.toml (try with and without the author parameter):
Solution
[params]
author = "Your Name"Add to layouts/_default/index.html:
Solution
<footer>
{{ if .Site.Params.author }}
<p>© 2025 {{ .Site.Params.author }}</p>
{{ else }}
<p>© 2025</p>
{{ end }}
</footer>