When working with Dynamics 365 / Dataverse Web Resources, one common challenge is ensuring users are aware when a JavaScript or HTML Web Resource has been updated. Browser caching can cause users to unknowingly run outdated versions, leading to unexpected behavior and hard-to-diagnose bugs.
In this article, we’ll look at a simple and effective technique to detect Web Resource changes at runtime using JavaScript hashing and notify users when an update is detected.
The Idea
The core idea is straightforward:
Download the Web Resource content
Calculate a hash (SHA-1) of the content
Store the hash in
localStorageCompare the current hash with the previously stored one
Notify the user if the hash has changed
This approach works entirely on the client side and requires no server-side customization.
Triggering the Check on Form Load
The check is executed when a Dynamics 365 form loads:
formContext.data.addOnLoad(
Opportunity.checkWebResourceHash.bind(this, "opportunity_webresource")
);
This ensures the verification runs automatically whenever the form is opened.
The Hash Comparison Function
Here’s the full implementation of the function responsible for detecting changes:
checkWebResourceHash: async function (webResourceName) {
const STORAGE_KEY = `WR_HASH_${webResourceName}`;
const STORAGE_DATE_KEY = `WR_HASH_DATE_${webResourceName}`;
try {
const clientUrl = Xrm.Utility.getGlobalContext().getClientUrl();
const url = `${clientUrl}/WebResources/${webResourceName}`;
// Fetch the Web Resource content without using cache
const response = await fetch(url, { cache: "no-store" });
const text = await response.text();
// Generate SHA-1 hash
const encoder = new TextEncoder();
const data = encoder.encode(text);
const hashBuffer = await crypto.subtle.digest("SHA-1", data);
const newHash = Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
const oldHash = localStorage.getItem(STORAGE_KEY);
// Detect changes
if (oldHash && oldHash !== newHash) {
const now = new Date().toISOString();
localStorage.setItem(STORAGE_KEY, newHash);
localStorage.setItem(STORAGE_DATE_KEY, now);
Xrm.Navigation.openAlertDialog({
title: "Web Resource Update",
text:
`The Web Resource "${webResourceName}" has been updated.\n\n` +
`Date: ${new Date(now).toLocaleString()}`
});
}
// First-time initialization
if (!oldHash) {
localStorage.setItem(STORAGE_KEY, newHash);
localStorage.setItem(STORAGE_DATE_KEY, new Date().toISOString());
}
} catch (e) {
console.error("Error while checking Web Resource", webResourceName, e);
}
}
Why SHA-1?
SHA-1 is not recommended for security purposes, but in this scenario it’s perfectly adequate:
We’re not securing sensitive data
We only need a fast and consistent checksum
It’s widely supported by the Web Crypto API
If you prefer, you can easily switch to SHA-256 by replacing "SHA-1" with "SHA-256".
Benefits of This Approach
No server-side changes
Works with any JavaScript or HTML Web Resource
Prevents silent cache-related issues
Improves transparency for users and testers
Easy to reuse across multiple forms and entities
Possible Enhancements
Automatically reload the page after detection
Display the last update date in a custom notification
Store hashes per environment (Dev / Test / Prod)
Extend the logic to multiple Web Resources at once