Here are the four methods with fully copy-pasteable, production-ready code for your single-page app architecture.
Method 1: Vanilla JS fetch() (Recommended)
This uses a tiny script to load your HTML fragments right when your app starts. It keeps everything strictly in the browser.
1. Create your component files:
Create header.html and footer.html in a folder called components/. (Just put the raw HTML inside them, no <html> or <body> tags needed).
2. Your index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My App</title>
</head>
<body>
<!-- Target containers for your includes -->
<header id="site-header"></header>
<main id="app-content">
<!-- Your existing JS will load JSON article data here -->
</main>
<footer id="site-footer"></footer>
<!-- The Loader Script -->
<script>
async function loadInclude(selector, filePath) {
const el = document.querySelector(selector);
if (!el) return;
try {
const res = await fetch(filePath);
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
el.innerHTML = await res.text();
} catch (err) {
console.error(`Failed to load ${filePath}:`, err);
el.innerHTML = `<!-- Failed to load ${filePath} -->`;
}
}
// Run as soon as the HTML shell loads
document.addEventListener('DOMContentLoaded', () => {
loadInclude('#site-header', 'components/header.html');
loadInclude('#site-footer', 'components/footer.html');
// ... Call your existing function that fetches the PHP-generated JSON here ...
});
</script>
</body>
</html>
- Pros: 100% decoupling from backend, pure
.html. - Cons: Cannot test by double-clicking the file locally (requires a local server like VS Code Live Server to bypass CORS).
Method 2: Modern Web Components
This creates custom HTML tags for your header and footer. It does the exact same thing as Method 1, but the code is heavily modular and highly modern.
1. The Component Script (app.js):
// Define a reusable class for external HTML components
class HTMLInclude extends HTMLElement {
async connectedCallback() {
const file = this.getAttribute('src');
if (!file) return;
try {
const res = await fetch(file);
if (res.ok) {
this.innerHTML = await res.text();
}
} catch (err) {
console.error(`Error loading component: ${file}`, err);
}
}
}
// Register the custom tag with the browser
customElements.define('html-include', HTMLInclude);
2. Your index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My App</title>
<!-- Load your components script first -->
<script src="app.js"></script>
</head>
<body>
<!-- Just use your new custom tag! -->
<html-include src="components/header.html"></html-include>
<main id="app-content"></main>
<html-include src="components/footer.html"></html-include>
</body>
</html>
- Pros: Extremely clean HTML syntax; highly scalable if you add more components later.
- Cons: Same local testing limitation as Method 1.
Method 3: Native PHP Includes
Since you have PHP on your shared host, you can simply change your index.html extension to .php. The server will stitch it together instantly before sending it to the user.
1. Your main file (MUST be renamed to index.php):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My App</title>
</head>
<body>
<!-- PHP pulls this in instantly on the server -->
<?php include 'components/header.html'; ?>
<main id="app-content">
<!-- Your JS injects the article JSON here -->
</main>
<!-- PHP pulls this in instantly on the server -->
<?php include 'components/footer.html'; ?>
<script src="app.js"></script>
</body>
</html>
- Pros: Zero Javascript needed for the layout. No FOUC (Flash of Unstyled Content). 100% success rate on shared hosting.
- Cons: You are no longer using purely
.htmlfiles. Requires a local PHP environment (like XAMPP orphp -S localhost:8000) to test on your computer.
Method 4: Apache .htaccess SSI
This is the true .shtml method, but configured to work on standard .html files.
1. Create a .htaccess file in your main public directory:
# Enable Server Side Includes
Options +Includes
# Tell the server to parse .html files for includes
AddType text/html .html
AddOutputFilter INCLUDES .html
2. Your index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My App</title>
</head>
<body>
<!-- Apache server will replace this before the browser sees it -->
<!--#include virtual="components/header.html" -->
<main id="app-content"></main>
<!--#include virtual="components/footer.html" -->
<script src="app.js"></script>
</body>
</html>
- Pros: Keeps the
.htmlextension, acts instantly on the server, no JS required for layout. - Cons: ~25% chance your shared host has disabled
mod_includefor security (if they did, adding the.htaccessfile will break your site with a 500 error). Like PHP, you cannot test this locally without running an Apache server.
TEXT Review
Here is a summary of all the methods that fit your exact setup (a single index.html SPA, pure JS/CSS/HTML frontend, on shared hosting), ranked by their ease of use for your specific architecture.
(Note: "% Success Rate" here means the likelihood it will work immediately in both your live shared hosting environment and on your local computer without tricky configuration.)
1. Vanilla JavaScript fetch() (The SPA Standard)
You use a tiny JS script in index.html to fetch header.html and inject it into a <div> or <header> tag when the page loads.
Success Rate: 99%
How it works:
document.querySelector('header').innerHTML = await (await fetch('header.html')).text();Drawbacks:
- FOUC (Flash of Unstyled Content): Because the HTML loads, then JS runs, then the header fetches, there may be a split-second delay where the header is missing before it pops in.
- Local Testing limitation: You cannot just double-click
index.htmlon your computer (file:///). Browser security (CORS) blocksfetch()from reading local files. You must use a local server (like VS Code's "Live Server") to test on your machine.
2. Modern JS Web Components (The Cleanest Code)
You register a custom HTML tag using JS (e.g., <site-header></site-header>), and the browser automatically fetches the HTML to fill it whenever it sees that tag.
Success Rate: 95%
How it works:
class SiteHeader extends HTMLElement { ... } customElements.define('site-header', SiteHeader);Drawbacks:
- Same issues as vanilla
fetch(): Has a split-second load delay and requires a local server to test. - Browser Support: Works on all modern browsers, but will fail entirely if a user is on an ancient browser (like Internet Explorer).
- Same issues as vanilla
3. Native PHP Includes (The Easiest Server Method)
Instead of fighting with .html, you simply rename your one page from index.html to index.php. Since you already have PHP on your shared host, this works out of the box.
Success Rate: 100% (On your live server)
How it works: Put
<?php include 'header.html'; ?>directly insideindex.php.Drawbacks:
- Breaks the "Pure HTML" rule: Your main file is now a
.phpfile, not.html. - Local Testing: To view your app locally, you now have to run a local PHP server (like XAMPP, MAMP, or
php -S) instead of just opening an HTML file.
- Breaks the "Pure HTML" rule: Your main file is now a
4. Apache SSI via .htaccess (True .shtml behavior)
You add a .htaccess file to your server telling it to process standard .html files for Server-Side Includes (SSI) before sending them to the browser.
Success Rate: ~75% (Depends heavily on your specific web host)
How it works:
<!--#include virtual="header.html" -->goes right inindex.html.Drawbacks:
- Hosting Restrictions: Many shared hosts disable
mod_include(SSI) for security or performance reasons. If they do, adding the.htaccesscode will either do nothing, or instantly crash your site with a500 Internal Server Error. - Local Testing: Your local computer does not run Apache by default. If you double-click
index.htmllocally, the<!--#include -->tag will just be ignored as an HTML comment, and your header won't load.
- Hosting Restrictions: Many shared hosts disable
The Verdict for your project:
Since you are building a Single Page Application (SPA) where JavaScript is already doing the heavy lifting (pulling JSON from your PHP bridge and rendering articles), Method 1 (Vanilla JS fetch) is by far the most natural fit.
It keeps your frontend 100% decoupled from the server, maintains the pure .html extension, and fits perfectly into the existing Javascript flow of your app.