Skip to main content

Inject ASCII Art in any HTML pages rendered by Docusaurus

Inject an ASCII-art banner into every generated Docusaurus HTML page using a lightweight postBuild plugin. Appears in page source or can be rendered visibly.

· 4 min read
View raw
Christophe
Markdown, WSL and Docker lover ~ Insatiable curious.
Cet article existe aussi en français — lire la version française.

Inject ASCII Art in any HTML pages rendered by Docusaurus

⚡
TL;DR

This article demonstrates how to inject a custom ASCII art banner into every HTML page generated by Docusaurus. It involves creating a lightweight postBuild plugin that scans generated files and inserts the ASCII art as an HTML comment after the <!doctype html> tag. The guide provides steps for creating the ASCII art file, the plugin, and configuring Docusaurus to activate it.

It might be entirely useless, and only the most dedicated tech-heads will spot it, but we're going to dive into how to inject a custom ASCII art banner at the very top of every HTML page generated for our blog.

Right now, just press CTRL+U (it's the same as View page source) to see what we'll do in this article.

The Result​

Once the plugin is in place (covered below), pressing CTRL+U on any page of the generated site shows this, right after the <!doctype html> tag:

HTML meerkat

Your website is demonstrably world-class and utterly unique in its field. ;-)

Why It Works​

  • The plugin hooks into Docusaurus's postBuild event — it only runs once yarn docusaurus build has finished writing HTML to disk.
  • It scans every generated .html file in the build output.
  • For each one, it injects the ASCII art as an HTML comment right after the <!doctype html> opening tag — invisible on the rendered page, visible only in the page source.

Unlike the remark plugins I've written before (see Creating a search&replace plugin for Docusaurus), this one doesn't touch the Markdown at all: it works on the generated HTML.

Installation​

First, we have to get some ASCII art. If you don't have one yet, you can use this online tool: Image to ASCII Art Converter. Just upload a small character on it and convert it. I've also covered the command-line way of producing such banners in Bash - ASCII art.

In your Docusaurus site, please create the src/data/banner.txt file and paste your ASCII art in it. Here is mine:



:+***###%%%%%%%%##****=:
.-*##%%%%%%##%%%%%%%%%%%%%%%%%%%%##+.
.=#%%%#%#%+.:..........::--------=#%%%%%%%%*:
.+#%%%##:........:------------------------+%%@@@@@:.
..=%#%##=......:----------------------------------*%%@@@#..
.:#%##*=.....:----:---------------------------=#@@%+=--*%@@@%-.
:####+.....:-+%@@@*=:-------------------------=@@@@@@@+----#@@@%=.
:#%%#+....:::*@@@@@@@@:-------------------------=**+-+##=------#@@@%-
.*#%%+....:::::=#%*--++-------------------------------------------=#@@@#:
-#%%*....:::::::::------------------------------------=++++++=-----===%@@#-
=#%%=...:::::::--=++++++----------------------------+************+=--===*@@%=
+%%%-...:-:::-++************+-----------------------******************====*@@@=.
+%%%-..:-----******************+--------------------****#%%%%%%##*******+===*@@@=
=%%%-..:----**********%@@@@@@****+------------------***%@@@%**%@@@@@*******===*@@@=
-#%%-..:----*******#@@@@%*=-=%@@@%**-----------------*#@#-::....::-#%@@#*****===*@@@%%=.
...=#%%=...:--=******#%@@+-::.....::-%@%*----------------*%-::+@@#-.....:%@@*****+==+#@@@%%%@:
.-%%%%%%%=..:---******#@@*-:....+%@@@%-:-%#----------------#==#:..+@@@%-....#@%*****+==*@@@##%%%*.
.-%%%%+*%%=..:---*******%@*:.......*@@@@@%--%*---------------==@@#-+%@@@@@-....@@*****+===#@@%#*%%%%.
.%%%#=.=%%%=..:--=******#@@*....**==%@@@@@@%:+*----------------*@@@@@@@@@@@-....@%******+==#@@@##+%@%+
.%%%#..-#@@@=..:--+******#%@*...:*@@@@@@@@@@%==#-------++++++=--=@@@@@@@@@@@-...=@%******+==+%@@##*%@@%
*%%#.:+*%@@#-.:---+*******#@*:...+@@@@@@@@@@%-#=---=*#*+=====*%*=-%@@@@@@@%-...=@%******+====#@@%##%@@%
%%%+.++#%@@*-::----********#@#-...-#@@@@@@%=:*+:::=#%#*+++++*#%%%-:=+*#+=-...+%@#******+-:===#@@@#*@@@%
%%%+=+##%@@#-------=*********%%*-...:------**-:...=@@%%%%%%@@@@@@#..:=++**#%##********+:..-==#@@@#%@@@:
%%%%++**#@@#=------.:**********#%%#####%*#*-:.....+@@@@@@@@@@@@@%-:....:=**********+:.....-==#@@%%%@%:
-@@@#+**#@@@+-----:...:*****************-:.........:*@@@@@@@@@@#=-:::....................:-=*%@@@@@%.
=@@@%+*#@@@+-----:.......-*+++****=:.................-#@@@@*=--::::=*...................:-=#@@@@+.
.%@@@#*%@@%=-----........................:@@#............:-::-:-=%@*:::...............:==*@@@-
.-%@@@@@@@*-----.........................:*%@#-:......:::::-=+@@#=::::::............:==+#@@*:
.=##%@@@+-----...........................-#%@@@%*===+#@@@%#*::::::::::.........:-==+%@@#:
+%@@*-----:.............................:+#******#-:::::::::::::::.......-===*@@@*:
=%@@#=-----:...................................::::::::::::::::::....:-====#@@%=.
-#@@@*------:................................:::::::::::::::::::::-====+#@@@#:
.-%@@@#-------::......................:::::::::::::::::::::::--=====#%@@@#- =+*+.
.-%@@@%#==-------::.........::::::::::::::::::::::::::---=====-#%@@@@#: +%@@%@@*
.:%@@@@%#+========------::::::::::::::::::::------======-*%@@@@@#:. .#@=--*@%-
.:*@@@@@@%#+--==--------------------------------====#@@@@@+:. :#@=---@@=
..:#@@@@@@@@%=------------------------------=-===#@@@: :#@---=@@=
.+%@@@=-----------------------------------*%@@: .#@%*-@@@@@@%+.
-#@@=------------------------------------#@@+. .*%%%*%@@=-=%=%@#-
.*%%-..--------------------:::::::-------#@@@: -#@=----=@%=*%=+#@*:
*%%#..----------------:..............:--+%@@:. +@@+==#%@@*#@#+*%@*:
+%%%:..-------------:..................:-#@@@%==: ..-+%%%*+++###%@@@%@@%-
:#%%:..:----------:......................*@@@@@@%%%%%%%%%%*::=++++++*#@@%+.
.=#%%%*-...:--------:........................#@@#+++*###*==:..:---===+++*@@#.
-#%%%#=...::---------:.........................=%@@*--::::::::---------==+@@%+
.-#%%%+:..::------------:..........................#@@@=-----------------===@@@*.
:#%%#=...::--------------:..........................-%@@%=-------------====#@@@*.
.#%%%+...:-----------------:..........................:+@@@+==---------====+%%@%:
.%%%*...:::-----------------:...........................-%@@%====--=======+%@@@%
.%%%*..:--------------=%=----:...........................--@@@#++++=====+#@@@@#.
%%%#..::------------+%%+-----:...........................:-%@@%+++++**%@@@@#+.
.%%#:..::----------#@@%*=-----:............................-=@@@@@@@@@@@%#+.
=%%#..-:----------%@@@=+=-----:............................-=#@@@@%#*+=.
:%%%-------------=+%@@#=------:............................-==@@%+
%%%#-------------=+%@@#------:............................:==@@@#
.@@%#=------------=+@@%+-----:.............................==*@@#:
.#@@%*=---------==++@@%-----:.............................===@@@*
.-%@@@%%%%%%%*++++*@@%-----:.............................===%@@*
..=@@@@@*+++++++@@%=-----:.............................-==+@@#-
=@@#+++++++%@@@+=----:..............................:==+@@@*
=@@#*++%@@@@@%===----:..............................:===%@@*
.=%@@@@@@%%++===-----:..............................:===*@@#:
.:=+%%@#*++--------:::..........:::-----+%@@#+++++===-----:...............................:===+@@@+
:+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@#+=====--------:..............................::===+@@@*
:#%%%******++++++**#######*++++=++++++++*#%%%=.:-------------:............................::::===+%@@*
-%%%#*********************-:::::::::::::=#%%+..:-------------:..........................::::::===+#@@*.
.+%%%#*******************=--------------*%%*:.::-------------:........................::::::::====+@@#:
.=%@@@%#****************=-------------+@@#=..:--------------:......................:::::::::-====+@@#-
.-#@@@%%*********####*=============+@@@+..:---------------:....................::::::::::-=====+@@@+
..+%@@@@@@#***#####+=============#@@*..:-----------------:................::::::::::::-======+@@@+
.#@@@@@@@@%###+============+@@@*.:-------------------:............::::::::::::::========+@@@+
.*%%@@@@@@@@@@%*=++++++#@@*..:---------------------:.......::::::::::::::--=====+===+@@@+
.:*##%@@@@@@@@@@@@@@@*.:-------------------------::::::::::::::::--============+@@@+
.:=+***##@@@*.:-----------------------======---------===========+=+===+@@@+
:@@%+:--------------------================================+===+@@@+
:@@%=------------------====================++++++++++++++=====+@@@+
.:@@#=---------------==========*###%@@@@@@@@@@@@%%##*+==+++====+@@%=
.-@@%=------------=========*#%@@@@@@@@##@@@@@@@@@@%##*+++++++++*@@#-
:@@%+----------========+#%@@@#=-... :@@@%++++++++++++++++++*@@#:
=#%%@@%*-------=========+#@@@*-. .-@@@#=++++++++++++++++*@@@@%%*:
=%%@@#:::------==========+%@@#:. .=@@%*+++++++++++++++++=-=*%@@@%-
#@@@....-----============+@@@# .*@@#++++++++++++++++++++=--*@@@:
.:%@@@@@@@@@@@@@@@@@@@@@@@@@@@# :%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#:
.+############################: :##############################:.

This is ASCII art depicting what appears to be a stylized owl or bird face. The image is constructed entirely from text characters like `@`, `%`, `#`, `*`, `+`, and `-` arranged to create shading and depth that resembles a creature with large eyes and a pointed beak or face. It's purely decorative and serves no programmatic function.

Create the plugin and load it​

Now, let's create the plugin. Please create plugins/ascii-injector/index.mjs and copy/paste the source code here below to your file:

/**
* Docusaurus Plugin: ascii-injector
*
* Purpose:
* This plugin will prepend an ASCII art banner (from a specified text file)
* in all generated HTML files as an HTML comment.
*
* Usage:
* - Save this plugin as `plugins/ascii-injector/index.mjs`
* - Create the file containing the ASCII art in `src/data/banner.txt`.
* - Add the plugin to `docusaurus.config.js` plugins array.
*
* Please see readme.md for more details
*/
import { promises as fs } from "node:fs";
import path from "node:path";
/**
* Recursively collect all HTML files in a given directory.
*/
async function collectHtmlFiles(dir) {
const entries = await fs.readdir(dir, { withFileTypes: true });
const files = await Promise.all(
entries.map(async (entry) => {
const fullPath = path.resolve(dir, entry.name);
if (entry.isDirectory()) return collectHtmlFiles(fullPath);
if (entry.isFile() && entry.name.endsWith(".html")) return [fullPath];
return [];
}),
);
return files.flat();
}
/**
* Docusaurus plugin that injects an ASCII art banner into all generated HTML files.
*
* @param {object} context - Docusaurus plugin context (unused).
* @param {object} options - Plugin configuration options.
* @param {string} options.bannerPath - Relative path (from siteDir) to the ASCII banner file.
*/
export default function asciiInjectorPlugin(context, options) {
if (!options || typeof options.bannerPath !== "string") {
throw new Error("[ascii-injector] Missing required option: bannerPath (string)");
}
return {
name: "ascii-injector-plugin",
async postBuild({ outDir, siteDir }) {
const bannerPath = path.resolve(siteDir, options.bannerPath);
let rawBanner;
try {
rawBanner = await fs.readFile(bannerPath, "utf8");
} catch (err) {
console.error(`[ascii-injector] Banner file missing: ${err.message}`);
return;
}
const commentBanner = `<!--\n${rawBanner}\n-->\n\n`;
const htmlFiles = await collectHtmlFiles(outDir);
console.log(
`[ascii-injector] Preparing to inject banner into ${htmlFiles.length} HTML files...`,
);
const results = await Promise.allSettled(
htmlFiles.map(async (filePath) => {
try {
const html = await fs.readFile(filePath, "utf8");
// proceed to inject; files are always regenerated during build
let updated;
// Default: insert comment after <!doctype ...>
const doctype = /<!doctype[^>]*>/i;
if (doctype.test(html)) {
updated = html.replace(doctype, (m) => `${m}\n${commentBanner}`);
} else {
// fallback to prepending comment
updated = commentBanner + html;
}
// Write atomically: temp file + rename avoids race conditions
// with other postBuild plugins that read HTML concurrently.
const tmpPath = `${filePath}.tmp`;
await fs.writeFile(tmpPath, updated, "utf8");
await fs.rename(tmpPath, filePath);
return { status: "ok", filePath };
} catch (err) {
return { status: "error", filePath, error: err };
}
}),
);
const ok = results.filter(
(r) => r.status === "fulfilled" && r.value && r.value.status === "ok",
).length;
const skipped = results.filter(
(r) => r.status === "fulfilled" && r.value && r.value.status === "skipped",
).length;
const failed =
results.filter(
(r) => r.status === "fulfilled" && r.value && r.value.status === "error",
).length + results.filter((r) => r.status === "rejected").length;
console.log(
`[ascii-injector] Injection complete — ok: ${ok}, skipped: ${skipped}, failed: ${failed}`,
);
},
};
}

This is a Docusaurus plugin that reads an ASCII art banner from a text file and injects it as an HTML comment into every generated HTML file in the build output. It does this by recursively finding all HTML files, reading the banner file, and then updating each HTML file — inserting the banner after the doctype declaration if one exists, or prepending it otherwise.

Almost done. Now, finalize the installation by editing your docusaurus.config.js file and, under plugins, please do this:

// [...]
plugins: [
// [...]
[
"./plugins/ascii-injector/index.mjs",
{ bannerPath: "src/data/banner.txt" },
],
],

This is a plugin configuration array that registers a custom plugin called 'ascii-injector' and passes it a configuration object specifying where to find a banner text file. The plugin will likely read that banner file and inject its ASCII art content somewhere in the build output.

The very last thing to do is to restart your Docusaurus website to load the updated configuration file.

More Demos​

Time to test​

Nothing new here, just generate the static version of your site.

>user@machine: ~/yourproject

Once your website is generated, just open any page and press CTRL+U and tadaaa, your ASCII art is there — exactly the result shown at the top of this article.

Under the Hood (skip this if you just want to use it)​

Not during preview mode

This plugin is only fired during the postBuild event i.e. only after your blog has been rendered as an HTML page. So if you're running Docusaurus in preview mode; you'll not see the ASCII art.

Conclusion​

A postBuild plugin, one ASCII art file, and a three-line config addition — and every page your site generates carries a small, pointless, entirely intentional signature in its source. If the command-line side of generating ASCII banners interests you more than the Docusaurus plumbing, Bash - ASCII art covers that half.


🦋 Join the conversation on BlueskyRequires a free Bluesky account to like, repost or comment. This block talks to Bluesky's public API directly from your browser (no cookies, no sign-in) — that means your IP address reaches Bluesky's servers just by this section loading.

Loading comments…

Was this article helpful?
Did you try the steps in this article?