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.