Contents

Modify Hugo's 404.html page

The cover image is the revised 404.html page.

Introduction

The default 404.html page in Hugo’s LoveIt theme is already simple and visually appealing. One day, I had a sudden idea: since the original 404.html displays a different emoticon every time it reloads, why not add a random message above the emoticon? So, I decided to give it a try! And that’s how this article came to be.

The Original 404.html

https://raw.githubusercontent.com/Josh-test-lab/website-assets-repository/refs/heads/main/posts/Modify%20Hugo's%20404.html%20page/old%20404%20page.en.png
he default `404.html` page in the LoveIt theme.

In the website’s root directory, under /layouts/404.html, we can find the following code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{{- define "title" }}
    {{- T "pageNotFound" | printf "404 %v" }} - {{ .Site.Title -}}
{{- end -}}

{{- define "content" -}}
    <div class="page" id="content-404">
        <h1 id="error-emoji"></h1>
        <p class="error-text">
            {{- T "pageNotFoundText" -}}&nbsp;
            <a href="javascript:void(0);" title="{{ T `back` }}" onclick="window.history.back();"><i class="far fa-hand-point-left fa-fw" aria-hidden="true"></i></a>
        </p>
    </div>
    <script type="text/javascript">
        (function() {
            var emojiArray = ['\\(o_o)/', '(˚Δ˚)b', '(^-^*)', '(≥o≤)', '(^_^)b', '(·_·)','(=\'X\'=)', '(>_<)', '(;-;)', '(T_T)'];
            document.getElementById('error-emoji').appendChild(document.createTextNode(emojiArray[Math.floor(Math.random() * emojiArray.length)]));
        })();
    </script>
{{- end -}}

This code contains a mix of Hugo’s underlying Go template syntax and HTML syntax. We can also see that all the emoticons that may appear are written here.

Modifying 404.html

Next, we will modify the 404.html page by adding text to make it more lively and engaging. The added text will include multilingual versions, making it usable on multilingual websites.

Creating Text Files

First, create a folder in the /static/ directory of the website’s root directory to store the displayed text and emoticons. Here, I name them 404 error text and 404 error emoji.

Next, create multilingual text files inside the /static/404 error text/ folder. Similarly, create text files for storing emoticons inside the /static/404 error emoji/ folder.

Tip
Since emoticons are universally understandable regardless of language, there is no need to create multilingual files for the text files storing emoticons.

The directory structure should look as follows.

1
2
3
4
5
6
7
8
9
/static/
│── 404 error text/       # Folder for storing 404 error message texts
│   ├── index.zh-tw.md    # Traditional Chinese 404 message
│   ├── index.en.md       # English 404 message
│   └── ...               # Other languages
│── 404 error emoji/      # Folder for storing 404 error emoticons
│   └── index.md          # Emoticon messages

We can place the various text messages we want to display in /static/404 error text/index.language_code.md. Here, I’ve used ChatGPT to assist in generating the following text.

  • index.zh-tw.md
1
2
3
4
5
你來到了未知的領域。
這裡什麼都沒有。
迷路的人最終會找到路。
這頁面可能被黑洞吞噬了。
...
  • index.en.md
1
2
3
4
5
You've entered an unknown territory.
There's nothing here.
Lost souls will eventually find their way.
This page might have been swallowed by a black hole.
...

Similarly, you can also place the emoticons in /static/404 error emoji/index.md.

  • index.md
1
2
3
4
5
(;-;)
(='X'=)
😁
😂
...

Modifying 404.html

Next, we can adjust the 404.html page. In addition to adding the functionality to display text, we will also modify the way emoticons are displayed, making it easier to update the text on the 404.html page.

First, add the text display. Above the <h1 id="error-emoji"></h1> element inside <div class="page" id="content-404">, add <h1 id="random-text" class="random-text" style="margin-top: 0;"></h1>. The purpose of this is to add a heading-level 1 element, aligned at the top, with a class tag of random-text above the emoticon.

After adding this, it should look like the following.

1
2
3
4
5
6
7
8
<div class="page" id="content-404">
    <h1 id="random-text" class="random-text" style="margin-top: 0;"></h1>
    <h1 id="error-emoji"></h1>
    <p class="error-text">
        {{- T "pageNotFoundText" -}}&nbsp;
        <a href="javascript:void(0);" title="{{ T `back` }}" onclick="window.history.back();"><i class="far fa-hand-point-left fa-fw" aria-hidden="true"></i></a>
    </p>
</div>

Next, modify the JavaScript script to randomly read a line from the files set up earlier in the #Creating Text Files section and output it based on the selected language.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script type="text/javascript">
    (function() {
        
        var lang = (document.documentElement.lang || 'en').toLowerCase(); // Get the current webpage language
        var textFilePath = `/404 error text/index.${lang}.txt`; // If the current webpage language is not the site's primary language, show the corresponding language text file
        var fallbackTextPath = '/404 error text/index.en.txt'; // Default text file
        var emojiFilePath = '/404 error emoji/index.txt'; // Emoticon file
        var fallbackEmojiPath = '/404 error emoji/index.txt'; // Default emoticon file

        function fetchRandomLine(filePath, fallbackPath, elementId) {
            fetch(filePath)
                .then(response => response.ok ? response.text() : fetch(fallbackPath).then(res => res.ok ? res.text() : Promise.reject()))
                .then(text => {
                    var lines = text.split('\n').filter(line => line.trim() !== '');
                    if (lines.length) {
                        document.getElementById(elementId).textContent = lines[Math.floor(Math.random() * lines.length)];
                    }
                })
                .catch(() => {
                    document.getElementById(`${elementId}`).textContent = `No content available for ${elementId}`; // Message for file not found (to be displayed on the webpage).
                });
        }

        fetchRandomLine(textFilePath, fallbackTextPath, 'random-text');
        fetchRandomLine(emojiFilePath, fallbackEmojiPath, 'error-emoji');
    })();
</script>

Therefore, the complete source code for the 404.html page should look like this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{{- define "title" }}
    {{- T "pageNotFound" | printf "404 %v" }} - {{ .Site.Title -}}
{{- end -}}

{{- define "content" -}}
    <div class="page" id="content-404">
        <h1 id="random-text" class="random-text" style="margin-top: 0;"></h1>
        <h1 id="error-emoji"></h1>
        <p class="error-text">
            {{- T "pageNotFoundText" -}}&nbsp;
            <a href="javascript:void(0);" title="{{ T `back` }}" onclick="window.history.back();"><i class="far fa-hand-point-left fa-fw" aria-hidden="true"></i></a>
        </p>
    </div>
    <script type="text/javascript">
        (function() {
            /*
            var emojiArray = ['\\(o_o)/', '(˚Δ˚)b', '(^-^*)', '(≥o≤)', '(^_^)b', '(·_·)','(=\'X\'=)', '(>_<)', '(;-;)', '(T_T)'];
            document.getElementById('error-emoji').appendChild(document.createTextNode(emojiArray[Math.floor(Math.random() * emojiArray.length)]));
            */
            
            var lang = (document.documentElement.lang || 'en').toLowerCase();
            var textFilePath = `/404 error text/index.${lang}.txt`;
            var fallbackTextPath = '/404 error text/index.en.txt';
            var emojiFilePath = '/404 error emoji/index.txt';
            var fallbackEmojiPath = '/404 error emoji/index.txt';

            function fetchRandomLine(filePath, fallbackPath, elementId) {
                fetch(filePath)
                    .then(response => response.ok ? response.text() : fetch(fallbackPath).then(res => res.ok ? res.text() : Promise.reject()))
                    .then(text => {
                        var lines = text.split('\n').filter(line => line.trim() !== '');
                        if (lines.length) {
                            document.getElementById(elementId).textContent = lines[Math.floor(Math.random() * lines.length)];
                        }
                    })
                    .catch(() => {
                        document.getElementById(`${elementId}`).textContent = `No content available for ${elementId}`;
                    });
            }

            fetchRandomLine(textFilePath, fallbackTextPath, 'random-text');
            fetchRandomLine(emojiFilePath, fallbackEmojiPath, 'error-emoji');
        })();
    </script>
{{- end -}}

With that, we have completed modifying the 404.html page.

Conclusion

With a simple modification to the 404.html page, every lost visitor will encounter a different webpage quote. Then, they can relax and head to the correct destination. If any readers have thoughts or suggestions, feel free to share them in the comments section.

Environment

  • Hugo 0.144.2
  • LoveIt theme (version from GitHub on February 21, 2025)