npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

web-version-watcher

v1.1.8

Published

A lightweight utility for automatically detecting and notifying web application updates using ETag/Last-Modified headers.

Readme

web-version-watcher

A lightweight utility for automatically detecting and notifying web application updates using ETag/Last-Modified headers.

简体中文 | English

Features

  • 🔄 Automatic update detection
  • 🚀 Zero dependencies
  • 💡 Customizable notification content and style
  • 🌐 Support for ETag and Last-Modified headers
  • 🔧 Configurable check interval
  • 📱 Responsive notification UI

Installation

npm install web-version-watcher

or

yarn add web-version-watcher

Usage

import VersionWatcher from 'web-version-watcher';

// Create instance
const watcher = new VersionWatcher({
  // Optional configuration
  checkInterval: 60000, // Check interval in milliseconds, default: 60s
  messages: {
    title: 'New Version Available',
    message: 'A new version of the application is available',
    buttonText: 'Refresh'
  }
});

// Start monitoring
watcher.start();

// Stop monitoring (if needed)
// watcher.stop();

// Manually show notification (for testing styles and functionality)
// watcher.show();

Configuration Options

WatcherOptions

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | checkInterval | number | 60000 | Check interval in milliseconds | | messages | object | function | - | Notification messages configuration, can be an object or a function that returns a messages object (supports dynamic multilingual content) | | messages.title | string | 'New Version Available' | Notification title (when messages is an object) | | messages.message | string | 'A new version of the application is available.' | Notification message (when messages is an object) | | messages.buttonText | string | 'Refresh' | Refresh button text (when messages is an object) | | disableInLocalhost | boolean | true | When true, version checks are disabled in localhost environment | | container.className | string | - | Custom CSS class name for notification container (appended to default class) | | container.style | string | - | Custom style string for notification container (appended to default styles) | | position | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom-right' | Notification position | | closable | boolean | true | Whether to show the close button | | showButton | boolean | true | Whether to show the refresh button | | onUpdate | (newTag: string, currentTag: string | null) => void | - | Update callback function. If provided, it will be called when an update is detected, and the default UI will not be shown. Users can completely customize UI and behavior in the callback | | checkUrl | string | (() => string) | window.location.pathname | Request URL for version checking. Can be a string path or a function that returns a path |

API Methods

start()

Start monitoring for version updates.

stop()

Stop monitoring for version updates.

show()

Manually show the notification (for testing). This method allows you to easily test the notification styles and functionality during local development without waiting for version checks or modifying files.

const watcher = new VersionWatcher({
  messages: {
    title: 'Test Notification',
    message: 'This is a test notification'
  }
});

// Show notification immediately for testing
watcher.show();

Completely Custom UI

Through the onUpdate callback function, you can completely customize the update notification UI and behavior. When an onUpdate callback is provided, the library will no longer display the default UI, but instead call your provided callback function.

Use Cases

1. Completely Custom UI

const watcher = new VersionWatcher({
  onUpdate: (newTag, currentTag) => {
    // Create custom modal
    const modal = document.createElement('div');
    modal.className = 'my-custom-modal';
    modal.style.cssText = `
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: white;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 4px 20px rgba(0,0,0,0.15);
      z-index: 10000;
    `;
    modal.innerHTML = `
      <h2>New Version Detected</h2>
      <p>Current version: ${currentTag || 'unknown'}</p>
      <p>New version: ${newTag}</p>
      <div style="margin-top: 20px; text-align: right;">
        <button onclick="window.location.reload()" style="margin-right: 10px;">Update Now</button>
        <button onclick="this.parentElement.parentElement.remove()">Later</button>
      </div>
    `;
    document.body.appendChild(modal);
  }
});

2. Using Third-party UI Library

import { ElNotification } from 'element-plus';
// or import { message } from 'antd';

const watcher = new VersionWatcher({
  onUpdate: (newTag) => {
    // Using Element Plus
    ElNotification({
      title: 'New Version Available',
      message: 'A new version is available, please refresh the page',
      type: 'info',
      duration: 0,
      onClick: () => window.location.reload()
    });
    
    // Or using Ant Design
    // message.info('New version available, please refresh', 0, () => {
    //   window.location.reload();
    // });
  }
});

3. Notification Only, No UI

const watcher = new VersionWatcher({
  onUpdate: (newTag) => {
    // Only log
    console.log('New version detected:', newTag);
    
    // Or send to analytics platform
    // analytics.track('version_update_detected', { newTag });
    
    // Or use browser native notification
    if ('Notification' in window && Notification.permission === 'granted') {
      new Notification('New Version Available', {
        body: 'A new version is available',
        icon: '/icon.png'
      });
    }
  }
});

4. Custom Refresh Logic

const watcher = new VersionWatcher({
  onUpdate: (newTag) => {
    if (confirm('New version detected, update now?')) {
      // Refresh immediately
      window.location.reload();
    } else {
      // Refresh after 5 seconds
      setTimeout(() => {
        window.location.reload();
      }, 5000);
    }
  }
});

How It Works

web-version-watcher periodically checks the page's ETag or Last-Modified headers to detect application updates. When an update is detected:

  • If an onUpdate callback is provided, it will be called (default UI will not be shown)
  • If no onUpdate callback is provided, it displays a notification at the specified position (default: bottom-right) of the page, allowing users to refresh immediately or postpone the update

Style Customization

CSS Variables

All notification styles can be customized using CSS variables. All variables have default values, and you can override them to customize the notification appearance.

Container Variables

| Variable | Default | Description | |----------|---------|-------------| | --wvw-background | white | Container background color | | --wvw-text-color | inherit | Container text color | | --wvw-border-radius | 4px | Container border radius | | --wvw-padding | 16px | Container padding | | --wvw-box-shadow | 0 2px 12px 0 rgba(0,0,0,.1) | Container box shadow | | --wvw-width | 330px | Container width | | --wvw-z-index | 9999 | Container z-index |

Title Variables

| Variable | Default | Description | |----------|---------|-------------| | --wvw-title-font-weight | bold | Title font weight | | --wvw-title-margin-bottom | 8px | Title bottom margin | | --wvw-title-color | #333 | Title color |

Message Variables

| Variable | Default | Description | |----------|---------|-------------| | --wvw-message-color | #666 | Message text color | | --wvw-message-margin-bottom | 12px | Message bottom margin |

Button Variables

| Variable | Default | Description | |----------|---------|-------------| | --wvw-button-bg | #225ED1 | Button background color | | --wvw-button-color | white | Button text color | | --wvw-button-padding | 8px 15px | Button padding | | --wvw-button-border-radius | 3px | Button border radius | | --wvw-button-font-size | 12px | Button font size |

Close Button Variables

| Variable | Default | Description | |----------|---------|-------------| | --wvw-close-color | #909399 | Close button color | | --wvw-close-size | 16px | Close button size |

Fixed Class Names

All notification elements use fixed class names for easy CSS selector targeting:

  • wvw_container - Notification container
  • wvw_title - Title
  • wvw_message - Message content
  • wvw_button-container - Button container
  • wvw_button - Refresh button
  • wvw_close-button - Close button

Style Customization Examples

Example 1: Customize via CSS Variables

/* In your CSS file */
.wvw_container {
  --wvw-background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  --wvw-text-color: white;
  --wvw-border-radius: 8px;
  --wvw-button-bg: #ff6b6b;
  --wvw-button-border-radius: 20px;
}

.wvw_title {
  font-size: 18px;
}

Example 2: Customize via Class Name and Style

const watcher = new VersionWatcher({
  container: {
    className: 'my-custom-notification',
    style: 'border: 2px solid #3498db;'
  }
});
/* Override child element styles via class name */
.my-custom-notification .wvw_button {
  background: #e74c3c !important;
  border-radius: 20px;
}

Example 3: Set Position and Button Display

const watcher = new VersionWatcher({
  position: 'top-right',  // Display at top-right corner
  closable: false,         // Hide close button
  showButton: false        // Hide refresh button
});

Example 4: Mixed Usage

const watcher = new VersionWatcher({
  position: 'bottom-left',
  closable: true,
  container: {
    className: 'dark-theme',
    style: '--wvw-background: #2c3e50; --wvw-text-color: white;'
  }
});

Local Development

Setup

  1. Clone the repository and install dependencies:
git clone <repository-url>
cd web-version-watcher
npm install
  1. Start the development build (watches for changes):
npm run dev

Running Examples

  1. Build the project first:
npm run build
  1. Start the local server:
npm run serve:example
  1. Open your browser and navigate to:
http://localhost:3000/examples/basic/

Testing Different Scenarios

  1. Testing Version Updates:

    • Open the example in your browser
    • Modify examples/basic/index.html (e.g., change some text)
    • Save the file
    • Wait for the check interval (default: 60 seconds)
    • You should see an update notification
  2. Testing Custom Messages:

    const watcher = new VersionWatcher({
      messages: {
        title: 'Custom Title',
        message: 'Custom Message',
        buttonText: 'Custom Button Text'
      }
    });

    Or use function form (supports dynamic multilingual content):

    const watcher = new VersionWatcher({
      messages: () => {
        // Get current language from external source
        const lang = getCurrentLanguage(); // Your language getter function
        const i18n = {
          'zh-CN': {
            title: '发现新版本',
            message: '应用有新版本可用',
            buttonText: '立即刷新'
          },
          'en-US': {
            title: 'New Version Available',
            message: 'A new version of the application is available.',
            buttonText: 'Refresh'
          }
        };
        return i18n[lang] || i18n['en-US'];
      }
    });
  3. Testing Position Configuration:

    const watcher = new VersionWatcher({
      position: 'top-left'  // Options: 'top-left', 'top-right', 'bottom-left', 'bottom-right'
    });
  4. Testing Close Button Configuration:

    const watcher = new VersionWatcher({
      closable: false  // Hide close button
    });
  5. Testing Refresh Button Configuration:

    const watcher = new VersionWatcher({
      showButton: false  // Hide refresh button
    });
  6. Completely Custom UI (using onUpdate callback):

    const watcher = new VersionWatcher({
      onUpdate: (newTag, currentTag) => {
        // Completely custom UI
        const modal = document.createElement('div');
        modal.className = 'my-custom-modal';
        modal.innerHTML = `
          <h2>New Version Detected</h2>
          <p>Current version: ${currentTag || 'unknown'}</p>
          <p>New version: ${newTag}</p>
          <button onclick="window.location.reload()">Update Now</button>
          <button onclick="this.parentElement.remove()">Later</button>
        `;
        document.body.appendChild(modal);
      }
    });
  7. Using Third-party UI Library:

    import { ElNotification } from 'element-plus';
        
    const watcher = new VersionWatcher({
      onUpdate: (newTag) => {
        ElNotification({
          title: 'New Version Available',
          message: 'A new version is available, please refresh the page',
          type: 'info',
          duration: 0,
          onClick: () => window.location.reload()
        });
      }
    });
  8. Notification Only, No UI:

    const watcher = new VersionWatcher({
      onUpdate: (newTag) => {
        // Only log, no UI
        console.log('New version detected:', newTag);
        // Or send to analytics platform
        analytics.track('version_update_detected', { newTag });
      }
    });
  9. Custom Request URL:

    // Use fixed path
    const watcher = new VersionWatcher({
      checkUrl: '/index.html'
    });
    
    // Use function to dynamically get path
    const watcher = new VersionWatcher({
      checkUrl: () => {
        // Can return path dynamically based on current route
        return window.location.pathname + '?v=' + Date.now();
      }
    });
    
    // Check specific resource file
    const watcher = new VersionWatcher({
      checkUrl: '/static/app.js'
    });
  10. Testing Different Intervals:

    const watcher = new VersionWatcher({
      checkInterval: 10000, // 10 seconds
    });
  11. Local Environment Behavior:

    • By default, version checks are disabled in localhost environment
    • This prevents unnecessary notifications during local development
    • To enable version checks in localhost:
      const watcher = new VersionWatcher({
        disableInLocalhost: false // Enable version checks in localhost
      });
  12. Testing Error Handling:

    • Try disconnecting from the network
    • Test with invalid response headers
    • Check the console for error messages

Browser Compatibility

Supports all modern browsers, including:

  • Chrome
  • Firefox
  • Safari
  • Edge

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT

See LICENSE for more details.