Skip to main content

Fix hashed filename when downloading a bundled PDF

Updated Jun 17, 2026 ·

Overview

When a PDF is downloaded from a React or Docusaurus page, the saved filename may include a generated hash.

Example:

Jose Eden-8cfb0a0ef0992e679b77e0160daa4035.pdf

Expected filename:

Jose Eden.pdf

This can happen when the PDF is imported as a bundled asset instead of being referenced as a static file path.

Root Cause

Bundlers often add a content hash to imported asset filenames. This is useful for browser caching because a changed file gets a new URL.

In this case, the resume PDF was loaded with require.context():

const pdfContext = require.context("../../../assets/site-design/resume", false, /\.pdf$/);
const moduleValue = pdfContext(pdfKeys[0]);
return typeof moduleValue === "string" ? moduleValue : moduleValue.default;

The generated URL points to a hashed file. When the link uses a bare download attribute, the browser may use the filename from that URL.

<a href={resumePdfUrl} className={styles.resumeButton} download>
Download full resume
</a>

Fix

Set an explicit filename on the download attribute.

const RESUME_DOWNLOAD_FILENAME = "Jose Eden.pdf";

<a
href={resumePdfUrl}
className={styles.resumeButton}
download={RESUME_DOWNLOAD_FILENAME}
>
Download full resume
</a>

This keeps the hashed asset URL for caching, but it tells the browser what filename to use when saving the file.

Why This Works

The download attribute can be used in two ways:

AttributeBrowser behavior
downloadDownload the file and infer the filename from the URL.
download="Jose Eden.pdf"Download the file using the provided filename.

The second form is better when the file URL is generated by a bundler or build tool.

Validation

After applying the change:

  1. Build or start the Docusaurus site.
  2. Open the homepage.
  3. Go to the Experiences section.
  4. Click Download full resume.
  5. Confirm the saved filename is Jose Eden.pdf.

Note: The URL can still contain a hash after this fix. The important part is the downloaded filename shown by the browser.

The fix was applied in:

src/components/homepage/Experiences.tsx