> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blaxel.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Standby control

> Control when sandboxes remain active by managing WebSocket connections, tab visibility, auto-disconnect behavior, and activity-based timeouts.

Sandboxes stay active as long as there's an active connection to them, typically through a WebSocket connection. When a browser tab becomes inactive, the WebSocket should disconnect after some time, but this behavior depends on the specific browser implementation.

<Note>
  You can also use [process keep-alive](./Processes#sandbox-keep-alive) to keep the sandbox running when you launch a process, even if there isn't an active connection to it.
</Note>

There are no built-in stop or start functions available in the SDKs to manage standby mode. This means you'll need to rely on other approaches to better control when sandboxes remain active:

1. Hide iframe when tab is inactive

   Use JavaScript events to detect when a user is not active on the tab:

   * Listen for tab visibility events that indicate when the user switches away from your tab
   * When the user becomes inactive, hide the iframe containing the sandbox preview
   * Show the iframe again when the user returns to the tab

2. Implement auto-disconnect on tab switch

   Some browsers (like Chrome) may keep WebSockets alive even when tabs are inactive to improve performance. You can implement an auto-disconnect feature that:

   * Detects when users switch tabs
   * Automatically disconnects the WebSocket connection
   * Reconnects when the user returns

3. Use activity-based timeouts

   Set up a timer system in your interface that:

   * Monitors user activity (typing, interactions, etc.)
   * Hides the preview after a period of inactivity
   * Prompts the user to confirm they're still using the sandbox

You can use the SDKs from the frontend if you have the right headers set on the session. Use the [`sandbox.sessions.create` function](/Sandboxes/Sessions) to manage sessions.

Here is an example of auto disconnect on tab switch with Vite through a plugin:

<Warning>
  The code below is illustrative and not intended for production use.
</Warning>

```typescript theme={null}
// vite.config.ts
import { defineConfig } from 'vite'
import { hmrVisibility } from './vite-plugin-hmr-visibility'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    hmrVisibility({
      disconnectDelay: 2000, // Wait 2 seconds before disconnecting
      debug: true, // Enable debug logging
    }),
  ],
  server: {
    // Prevent Vite from aggressive reconnection attempts
    hmr: {
      overlay: false, // Disable error overlay which can trigger reconnections
    },
  },
})

// vite-plugin-hmr-visibility.ts
/**
 * Vite Plugin: HMR Visibility Manager
 * Automatically disconnects/reconnects HMR websocket based on tab visibility
 * to reduce serverless billing costs
 */

import type { Plugin } from 'vite'

export interface HMRVisibilityOptions {
  /**
   * Grace period before disconnecting (ms)
   * @default 2000
   */
  disconnectDelay?: number

  /**
   * Enable debug logging
   * @default false
   */
  debug?: boolean
}

export function hmrVisibility(options: HMRVisibilityOptions = {}): Plugin {
  const {
    disconnectDelay = 2000,
    debug = false,
  } = options

  return {
    name: 'vite-plugin-hmr-visibility',
    apply: 'serve', // Only apply in dev mode

    transformIndexHtml() {
      // Inject the HMR visibility manager as an inline script
      return [
        {
          tag: 'script',
          attrs: { type: 'module' },
          children: `
// HMR Visibility Manager - Disconnects HMR when tab is hidden
(function() {
  const log = (...args) => {
    if (${JSON.stringify(debug)}) {
      console.log('[HMR Visibility]', ...args);
    }
  };

  let disconnectTimer = null;
  let isTabVisible = !document.hidden;
  const DISCONNECT_DELAY = ${JSON.stringify(disconnectDelay)};

  // Store reference to original functions
  const OriginalWebSocket = window.WebSocket;
  const OriginalSetTimeout = window.setTimeout;
  const OriginalSetInterval = window.setInterval;

  let hmrSocket = null;
  let shouldBlockReconnect = false;
  let blockedTimers = new Set();

  // Patch setTimeout and setInterval to block Vite's reconnection timers
  window.setTimeout = function(callback, delay, ...args) {
    // Block timers when tab is hidden and it looks like a reconnection attempt
    if (shouldBlockReconnect && delay && delay >= 500 && delay <= 5000) {
      const callbackStr = callback.toString();
      if (callbackStr.includes('connect') || callbackStr.includes('WebSocket') || callbackStr.includes('ws')) {
        log('Blocked reconnection timer (setTimeout)');
        const timerId = OriginalSetTimeout(() => {}, 999999999);
        blockedTimers.add(timerId);
        return timerId;
      }
    }
    return OriginalSetTimeout.call(this, callback, delay, ...args);
  };

  window.setInterval = function(callback, delay, ...args) {
    // Block intervals when tab is hidden
    if (shouldBlockReconnect) {
      const callbackStr = callback.toString();
      if (callbackStr.includes('connect') || callbackStr.includes('WebSocket') || callbackStr.includes('ws')) {
        log('Blocked reconnection interval (setInterval)');
        const timerId = OriginalSetInterval(() => {}, 999999999);
        blockedTimers.add(timerId);
        return timerId;
      }
    }
    return OriginalSetInterval.call(this, callback, delay, ...args);
  };

  // Patch WebSocket to track and block HMR connections
  window.WebSocket = function(url, protocols) {
    // Block new WebSocket connections when tab is hidden
    if (shouldBlockReconnect) {
      log('Blocked WebSocket connection attempt while tab is hidden:', url);
      // Return a fake WebSocket that stays in CONNECTING state forever
      const fakeWs = {
        readyState: 0, // CONNECTING (keeps Vite waiting)
        close: () => { log('Fake WebSocket close called'); },
        send: () => { log('Fake WebSocket send called'); },
        addEventListener: () => {},
        removeEventListener: () => {},
        dispatchEvent: () => false,
        onopen: null,
        onclose: null,
        onerror: null,
        onmessage: null,
      };
      return fakeWs;
    }

    const ws = new OriginalWebSocket(url, protocols);

    // Track HMR WebSocket and intercept its event handlers
    if (typeof url === 'string') {
      hmrSocket = ws;
      log('HMR WebSocket created and tracked');

      // Intercept the onclose setter to control reconnection behavior
      let userOnClose = null;
      Object.defineProperty(ws, 'onclose', {
        get() { return userOnClose; },
        set(handler) {
          userOnClose = function(event) {
            // If we're blocking reconnect, don't call Vite's onclose handler
            if (shouldBlockReconnect) {
              log('Suppressed onclose handler - blocking reconnection');
              return;
            }
            // Otherwise, call the original handler
            if (handler) {
              handler.call(this, event);
            }
          };
        },
        configurable: true
      });

      // Also intercept addEventListener for 'close' events
      const originalAddEventListener = ws.addEventListener;
      ws.addEventListener = function(type, listener, options) {
        if (type === 'close') {
          const wrappedListener = function(event) {
            if (shouldBlockReconnect) {
              log('Suppressed close event listener - blocking reconnection');
              return;
            }
            listener.call(this, event);
          };
          return originalAddEventListener.call(this, type, wrappedListener, options);
        }
        return originalAddEventListener.call(this, type, listener, options);
      };
    }

    return ws;
  };

  // Copy static properties from original WebSocket
  Object.setPrototypeOf(window.WebSocket, OriginalWebSocket);
  window.WebSocket.prototype = OriginalWebSocket.prototype;

  // Copy static constants
  Object.defineProperty(window.WebSocket, 'CONNECTING', { value: 0, enumerable: true });
  Object.defineProperty(window.WebSocket, 'OPEN', { value: 1, enumerable: true });
  Object.defineProperty(window.WebSocket, 'CLOSING', { value: 2, enumerable: true });
  Object.defineProperty(window.WebSocket, 'CLOSED', { value: 3, enumerable: true });

  const disconnectHMR = () => {
    if (hmrSocket && (hmrSocket.readyState === 0 || hmrSocket.readyState === 1)) {
      log('Disconnecting HMR websocket (tab hidden) - saving costs ✅');
      hmrSocket.close(1000, 'Tab hidden');
      hmrSocket = null;
    }
    shouldBlockReconnect = true;
  };

  const reconnectHMR = () => {
    shouldBlockReconnect = false;

    // Clear any blocked timers
    blockedTimers.forEach(timerId => {
      try {
        clearTimeout(timerId);
        clearInterval(timerId);
      } catch (e) {}
    });
    blockedTimers.clear();

    // If HMR was disconnected, reload to reconnect
    if (!hmrSocket || hmrSocket.readyState === 3) {
      log('Reloading page to restore HMR connection');
      setTimeout(() => window.location.reload(), 100);
    }
  };

  // Handle tab visibility changes
  document.addEventListener('visibilitychange', () => {
    isTabVisible = !document.hidden;

    if (document.hidden) {
      log('Tab hidden - will disconnect HMR in ' + DISCONNECT_DELAY + 'ms');

      disconnectTimer = OriginalSetTimeout(() => {
        if (document.hidden) {
          disconnectHMR();
        }
      }, DISCONNECT_DELAY);

    } else {
      log('Tab visible - allowing HMR reconnection');

      // Clear disconnect timer if tab becomes visible again
      if (disconnectTimer) {
        clearTimeout(disconnectTimer);
        disconnectTimer = null;
      }

      OriginalSetTimeout(() => {
        reconnectHMR();
      }, 100);
    }
  });

  // Handle page freeze events
  window.addEventListener('freeze', () => {
    log('Page freeze detected - disconnecting HMR immediately');
    disconnectHMR();
  }, { capture: true });

  window.addEventListener('resume', () => {
    log('Page resume detected - allowing HMR reconnection');
    OriginalSetTimeout(() => {
      reconnectHMR();
    }, 100);
  }, { capture: true });

  log('HMR Visibility Manager initialized - will block reconnection attempts when tab is hidden');
})();
          `,
          injectTo: 'head-prepend',
        },
      ]
    },
  }
}
```
