Blog Site: Reusable Header and Footer
Published: October 6, 2025
To avoid repeating the same header and footer markup on every page, I created a JavaScript file that dynamically loads the header and footer from separate HTML files.
Here’s the full script:
document.addEventListener('DOMContentLoaded', () => {
const depth = location.pathname.split('/').length - 2;
const prefix = '../'.repeat(depth);
const inject = async (targetId, path) => {
try {
const res = await fetch(prefix + path);
if (res.ok) {
const html = await res.text();
document.getElementById(targetId).innerHTML = html;
} else {
console.warn(`Failed to load ${prefix + path}: ${res.status}`);
document.getElementById(targetId).innerHTML = '<p>Navigation failed to load.</p>';
}
} catch (err) {
console.error(`Error loading ${prefix + path}`, err);
document.getElementById(targetId).innerHTML = '<p>Navigation failed to load.</p>';
}
};
inject('site-header', 'partials/header.html');
inject('site-footer', 'partials/footer.html');
});
This script runs after the DOM is fully loaded. It fetches the
header and footer from the partials/ folder, using a
depth-aware prefix to ensure correct paths whether the page is at
the root or nested. This makes the script work both locally and
when deployed to platforms like Vercel. It injects the fetched
content into the page using placeholder
<div> elements with IDs
site-header and site-footer.
The inject.js file lives in the
js/ folder, and the
partials/
folder sits at the root level. This setup keeps shared components
easy to locate and update.
I also added a <noscript> block to provide
fallback messaging for users who have JavaScript disabled. It
ensures they understand why navigation and footer content may be
missing.