Single-Page Research With Vanilla JS

Fetching reports, not frameworks

2 min read

Every report started as a standalone HTML file at /research/some-report/ with its own layout. Click a link, get a full page refresh. Fine for a few pages, but frustrating when flipping between them.

I wanted a single-page feel without rebuilding in a framework.

The hybrid approach

Reports still live at src/research/<slug>/index.html with frontmatter, so Eleventy collects them and their metadata. But the layout now redirects instantly:

<script>window.location.replace('/research/#research-page-hybrid-approach');</script>

Land on /research/excel-minis/ and you're sent to /research/#excel-minis. The report page exists as a fetch target, not a destination.

Click a sidebar link on the research page and vanilla JS fetches the report, extracts .blog-content, and swaps it in:

fetch(url)
    .then(res => res.text())
    .then(html => {
        const doc = new DOMParser().parseFromString(html, 'text/html');
        const blogContent = doc.querySelector('.blog-content');
        if (blogContent) {
            blogContent.querySelectorAll('img, iframe').forEach(el => {
                const src = el.getAttribute('src');
                if (src && src.startsWith('./'))
                    el.setAttribute('src', url + src.slice(1));
            });
            content.innerHTML = blogContent.outerHTML;
        }
    });

Fetch, parse, swap. No router, no state management, no virtual DOM.

Why vanilla JS

The requirements were small: fetch content, fix relative paths, update the URL hash, highlight the sidebar. About 50 lines of logic. Adding a framework would have been more complexity than the problem itself.

Downside: no loading spinners or transitions. Click a link, see "Loading...", then the content. Fast enough that it doesn't matter.

Deep linking for free

The URL hash maps to the report slug (#excel-minis, etc.), so sharing a link lands you on the exact report with no extra routing. The redirect ties old standalone URLs into the same system.

Future reports

New reports are just src/research/<slug>/index.html with frontmatter. Passthrough copy handles images and downloads. The sidebar populates automatically from the Eleventy collection. No JS changes needed.

The hybrid gives me build-time metadata from a static site generator with a lightweight SPA feel. More reports won't slow anything down -- it's just fetching text and swapping a div.