I got a SPFX WebPart for Sharepoint online that seems to be working fine, but now we notice that when i deploy a update some of the users keep getting the old versions, and hard refresh does not always work on the first time, they have to do it multiple times for it to be updated. So i am looking for a way to make my app always request the latest version.

I have no control on the sharepoint configuration all i can control y my SPFX webpart code, so i am looking at any piece of code i could add to my SPFX webpart or any of the configuration files of it or the manifest to make this happen.

I have tried a few examples i found online where they changed the manifest version or other parts, but none seem to work.

here is the skeleton of my code,

    import * as React from 'react';
    import styles from '../../common/SpfxWebPart.module.scss';
    import type { ITagQueryGeneratorWebPartProps } from './ITagQueryGeneratorWebPartProps';
    import { debounce, escape } from '@microsoft/sp-lodash-subset';
    import { SPFI } from "@pnp/sp";
    import "@pnp/sp/webs";
    import "@pnp/sp/fields";
    import "@pnp/sp/lists";
    import "@pnp/sp/presets/all";
    import "@pnp/sp/items";
    import { getSP } from '../../../pnpjsConfig';
    import { SearchResults, ISort, SortDirection } from "@pnp/sp/search";
    import { ListView, Pagination, SelectionMode } from '@pnp/spfx-controls-react';
    import { Autocomplete, Box, Paper, TextField } from '@mui/material';
    import ReactLoading from 'react-loading';
    //import { MultiSelect } from 'react-multi-select-component';
    export interface ITagQueryGeneratorWebPartState {
      searchResults: SearchResults | undefined,
      resultDocs: ISPDocument[];
      isLoaded: boolean;
      isSearchDone: boolean;
      currentPage: number;
      totalPages: number;

    const loadingTimeoutSecond = 2;
    export default class TagQueryGeneratorWebPart extends React.Component<ITagQueryGeneratorWebPartProps, ITagQueryGeneratorWebPartState> {
      private _sp: SPFI;
      private debouncedHandleSearch: () => void;
      constructor(props: ITagQueryGeneratorWebPartProps) {
        this._sp = getSP();
        this.state = {
          resultDocs: [],
          sites: [],
          searchResults: undefined,
          isLoaded: false,
          isSearchDone: true,
          currentPage: 0,
          totalPages: 0
        this.debouncedHandleSearch = debounce(this.handleSearch.bind(this), 1500);
        console.log(getCurrentTimestamp(), ":");
      componentDidMount(): void {
        setTimeout(() => {
          this.setState({ isLoaded: true });
        }, loadingTimeoutSecond * 1000);
      private renderInput = (label: string, fieldName: keyof ITagQueryGeneratorWebPartState, value: string, id: string): JSX.Element => (
        <div className={styles.divFlex}>
          <div className={styles.divLabel}>
            <label htmlFor={id} className={styles.textlabel}>{label}</label>
          <div className={styles.divInput}>
            <input id={id} className={styles.textfield} type="text" value={value}
              onChange={this.handleInputChange(fieldName)} placeholder={label} />
      private renderLoading = (pWidth: number = 100, pHeigth: number = 520): JSX.Element => (
        <div className={styles.WPTQLoadingDiv}>
          <ReactLoading type="spinningBubbles" color="#0078D4" height={pHeigth} width={pWidth} className={styles.WPTQLoadingIcon} />
      private renderListView = (resultDocs: ISPDocument[]): JSX.Element => (
        <ListView items={resultDocs}
      public render(): React.ReactElement<ITagQueryGeneratorWebPartProps> {
        const {
        } = this.props;
        return (
          <section className={`${styles.webPart} `} >
              this.state.isLoaded ? (
                <div className={styles.WPTQGMainDiv} >
                  <h2 className={styles.WPTitle}>{escape(description)}</h2>
                  <div className={styles.divInline}>
                  <div id='contextMenuDiv' className={styles.WPTQGDivListView} >
                    {this.state.isSearchDone ? this.renderListView(this.state.resultDocs) : this.renderLoading()}
                      onChange={(page) => this.handlePageChange(page)}
              ) : this.renderLoading()
          </section >

1 Answer 1


To ensure your SharePoint Framework (SPFx) Web Part always requests the latest version after updates, you can implement cache-busting techniques. Here’s a concise approach:

  1. Cache-Control Headers: Configure your web server to set appropriate Cache-Control headers to ensure browsers fetch the latest version of your web part files.

  2. Versioning: Update the version number in your web part manifest (package-solution.json) or in the URLs of your bundled JavaScript and CSS files whenever you deploy a new version.

  3. Dynamic Loading: Use dynamic imports or asynchronous loading techniques for your JavaScript and CSS files to prevent browsers from caching old versions aggressively.

  4. Service Worker (Optional): If applicable and carefully managed, use a service worker to control caching behavior and ensure updated content is served.

  5. Deployment Best Practices: Ensure your deployment process includes steps to clear any server-side or CDN caches that might cache old versions of your files.

Implementing these strategies should help ensure that users consistently receive the latest version of your SPFx web part after updates without needing multiple hard refreshes.

  • 1. I dont have access to control the headers of the webserver, i can only create the webpart package and send it to be deployed. 2. I update the versions in the manifest and it still does this. 3. can you show me a example of this?
    – rread
    Commented Jul 2 at 22:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.