Join our WhatsApp Channel
Cart / ₹0.00

No products in the cart.

Visit Support Portal
No Result
View All Result
Monday, October 12, 2026
  • Home
  • Tutorials
    • Machine Learning
    • Python
    • Web Development
    • Javascript
    • Mathematics
    • Deep Learning
    • Artificial Intelligence
  • Store
  • Our ServicesBook Now
    • Managed Hosting Services
    • Web Development Services
    • Digital Marketing Services
  • Our Softwares
    • JaggoRe AIFree
    • Email ExtractorPaid
    • TimeWell AppFree
Get a Free Quote
  • Login
  • Register
Neuraldemy
Monday, October 12, 2026
Cart / ₹0.00

No products in the cart.

No Result
View All Result
Neuraldemy
Get A Free Quote
Home Javascript

Handling Bfcache with Active WebSocket Connections in React: A Complete Tutorial

Neuraldemy by Neuraldemy
October 12, 2026
in Javascript, Web Development
Reading Time: 18 mins read
A A

The browser’s Back/Forward Cache (bfcache) freezes entire pages in memory so back-and-forward navigation feels instantaneous. However, an open WebSocket connection prevents browsers like Safari and Chrome from caching the page, forcing sluggish full-page reloads.

Even worse, if a page is cached with a severed socket, navigating back leaves the connection dead because React useEffect does not re-run. To fix this, listen to the browser’s pagehide event to gracefully close the socket before freeze, and use the pageshow event with event.persisted to reconnect the socket the moment the page thaws.

Table of Contents

  • The Problem in Simple Words
  • Common Workarounds
    • Workaround 1: Disabling bfcache Entirely
    • Workaround 2: Relying on the unload Event
  • What Developers Commonly Do Wrong in React
  • The Solution For Bfcache Issues
    • How this hook works:
  • Using the Hook in a Real Component
  • Conclusion & Key Takeaways

The Problem in Simple Words

Have you ever clicked the “Back” button on a website, and the previous page appeared instantly with zero delay, exactly where you left off?

That speed is powered by a browser feature called the bfcache (Back/Forward Cache).

Unlike regular HTTP caching, which only stores downloaded files like images and CSS, the bfcache saves the entire living page including the DOM tree, scroll position, and JavaScript memory. When you click Back, the browser does not rebuild the page from scratch. It pauses the page when you leave, and unfreezes it when you return.

This creates an app-like experience. But there is a major catch: live network connections.

If your app uses a WebSocket for live chat, stock tickers, or notification counters the browser cannot keep an open TCP socket connected while the page is asleep in memory.

When a user navigates away from your page:

  • The browser sees an active WebSocket.
  • Browsers like Safari and Chrome must make a choice: either discard the bfcache and force a slow reload, or freeze the page and kill the socket.
  • If the browser freezes the page and kills the socket, returning leaves your user with a dead connection. The screen looks normal, but no new messages ever arrive.

Common Workarounds

When developers notice that live features break after using the Back button, they usually try one of two common fixes. Both create serious problems.

Workaround 1: Disabling bfcache Entirely

Some developers give up and deliberately prevent caching by sending no-store headers or adding dummy unload listeners.

Why it hurts:

This “fixes” the stale socket bug by destroying navigation speed. Every time users tap Back, their browser shows a white flash, re-downloads assets, and re-executes JavaScript. On mobile connections, this wastes data and ruins the user experience.

Workaround 2: Relying on the unload Event

Other developers try to clean up their WebSocket inside an unload event listener.

Why it hurts:

Adding an unload event listener permanently disqualifies your page from bfcache. In Firefox and Safari, attaching an unload handler stops the browser from caching that page entirely. Modern web standards explicitly recommend avoiding unload.

What Developers Commonly Do Wrong in React

In standard React code, developers initialize WebSockets inside useEffect:

useEffect(() => {
  const socket = new WebSocket('wss://example.com/feed');

  socket.onmessage = (event) => {
    setMessages((prev) => [...prev, event.data]);
  };

  return () => {
    socket.close();
  };
}, []);

Here is why this fails in production:

  1. useEffect cleanup only runs when components unmount.When a user clicks an external link or navigates away, the browser often freezes the page directly into the bfcache before React unmounts your components.
  2. useEffect setup does NOT re-run on back navigation.When the user clicks “Back”, the browser restores the existing component instance straight out of memory. Because the component was never unmounted or re-mounted, useEffect never runs again.
  3. The result:Your socket object remains in memory with a closed status (readyState = 3). The user sees their chat window or notification bell, but the app is completely silent until a manual page refresh.

The Solution For Bfcache Issues

To keep both instant back navigation and live WebSockets, we must coordinate with the browser’s Page Lifecycle.

Modern browsers provide two events specifically built for this: pagehide and pageshow.

Here is the strategy:

  1. Before the page freezes (pagehide):When the user navigates away, pagehide fires. We immediately close the WebSocket with a clean status code (1000 Normal Closure). Because the page now has zero active sockets, the browser happily stores it in the bfcache.
  2. When the page returns (pageshow):When the user clicks Back, pageshow fires with a special property called event.persisted.
  3. The Check (event.persisted === true):If persisted is true, the browser tells us the page just woke up from the bfcache. We immediately create a fresh WebSocket connection and fetch any updates missed while asleep.

Here is a custom React hook called useBfCacheWebSocket:

import { useEffect, useRef, useState, useCallback } from 'react';

interface Options {
  url: string;
  onMessage?: (data: string) => void;
  onRestore?: () => void;
}

export function useBfCacheWebSocket({ url, onMessage, onRestore }: Options) {
  const [isConnected, setIsConnected] = useState(false);
  const socketRef = useRef<WebSocket | null>(null);

  const connect = useCallback(() => {
    if (socketRef.current?.readyState === WebSocket.OPEN) return;

    const socket = new WebSocket(url);
    socketRef.current = socket;

    socket.onopen = () => setIsConnected(true);
    socket.onmessage = (event) => onMessage?.(event.data);
    socket.onclose = () => setIsConnected(false);
  }, [url, onMessage]);

  const disconnect = useCallback(() => {
    if (socketRef.current) {
      socketRef.current.close(1000, 'Page entering bfcache');
      socketRef.current = null;
      setIsConnected(false);
    }
  }, []);

  useEffect(() => {
    connect();

    const handlePageHide = () => disconnect();
    const handlePageShow = (event: PageTransitionEvent) => {
      if (event.persisted) {
        connect();
        onRestore?.();
      }
    };

    window.addEventListener('pagehide', handlePageHide);
    window.addEventListener('pageshow', handlePageShow);

    return () => {
      window.removeEventListener('pagehide', handlePageHide);
      window.removeEventListener('pageshow', handlePageShow);
      disconnect();
    };
  }, [connect, disconnect, onRestore]);

  return { isConnected };
}

How this hook works:

  1. connect() and disconnect(): These helpers open and close the WebSocket cleanly without leaving dangling references.
  2. handlePageHide: Fires right before the page enters the bfcache. It calls disconnect(), freeing the socket so the browser caches the page.
  3. handlePageShow: Checks event.persisted. If true, the page woke up from memory. It calls connect() to rebuild the socket and triggers onRestore() to refresh stale data.
  4. Standard Cleanup: If the component unmounts during regular SPA navigation, listeners are removed and the socket is closed.

Using the Hook in a Real Component

Here is a practical live notification badge that stays connected across back and forward navigation:

import React, { useState, useCallback } from 'react';
import { useBfCacheWebSocket } from './useBfCacheWebSocket';

export function LiveNotificationBell() {
  const [unreadCount, setUnreadCount] = useState(0);

  const handleMessage = useCallback((data: string) => {
    try {
      const payload = JSON.parse(data);
      if (payload.type === 'NEW_NOTIFICATION') {
        setUnreadCount((count) => count + 1);
      }
    } catch {
      // Ignore invalid payloads
    }
  }, []);

  const handleBfCacheRestore = useCallback(async () => {
    const res = await fetch('/api/notifications/unread-count');
    if (res.ok) {
      const data = await res.json();
      setUnreadCount(data.count);
    }
  }, []);

  const { isConnected } = useBfCacheWebSocket({
    url: 'wss://example.com/notifications',
    onMessage: handleMessage,
    onRestore: handleBfCacheRestore,
  });

  return (
    <div className="notification-bell">
      <span className={isConnected ? 'status online' : 'status offline'} />
      <span className="count">{unreadCount}</span>
    </div>
  );
}

When a user navigates away and clicks Back:

  • The page opens with zero loading time.
  • The WebSocket reconnects instantly in the background.
  • onRestore fetches any unread notifications missed while the tab was asleep.

Conclusion & Key Takeaways

WebSockets and bfcache do not have to fight each other. With the right lifecycle events, you get both real-time updates and instant back navigation.

Key takeaways:

  • Never use window.addEventListener('unload'); it permanently breaks bfcache.
  • Use pagehide to close open WebSockets before the browser takes a page snapshot.
  • Use pageshow with event.persisted to reconnect the socket when the user returns.
  • Always provide an onRestore callback to sync missed messages or updates.

How can I test if bfcache is working on my site?

Open Chrome DevTools, navigate to Application, and select Back/forward cache. Click Test back/forward cache. DevTools tests navigation and lists any reasons preventing caching.

Does document.visibilityState replace pagehide?

No. visibilitychange fires when switching tabs or minimizing the browser window. The pagehide event specifically signals that the document is being unloaded or frozen into bfcache.

Does this also apply to Server-Sent Events (SSE)?

Yes. Open EventSource (SSE) connections cause the same bfcache eviction behavior as WebSockets. Close your EventSource on pagehide and recreate it on pageshow.

Support

Buy author a coffee

Support
SummarizeSendShareTweetScan
Previous Post

Preventing Out-of-Order Rollbacks in Optimistic UI: A Step-by-Step React Tutorial

Next Post

Preventing Infinite Echo Loops in React Broadcast Channel

Neuraldemy

Neuraldemy

This is Neuraldemy support. Subscribe to our YouTube channel for more.

Related Posts

Preventing Infinite Echo Loops in React Broadcast Channel

Preventing Out-of-Order Rollbacks in Optimistic UI: A Step-by-Step React Tutorial

100% Working Hostinger Discount Coupons & Promo Codes

100% Working Hostinger Discount Coupons & Promo Codes (2026)

Honest Review: Data Structures And Algorithms Made Easy By Narasimha Karumanchi

JavaScript Concepts Revision For Interview

Git Tutorial: A Beginners Guide To Git And GitHub

Next Post

Preventing Infinite Echo Loops in React Broadcast Channel

Support

Buy author a coffee

Support

Support Neuraldemy

Newsletter

Top rated products

  • Probability and Statistics for Machine Learning and Data Science Probability and Statistics for Machine Learning and Data Science
    Rated 5.00 out of 5
    ₹599.00 Original price was: ₹599.00.₹349.00Current price is: ₹349.00.
  • SVM Notes: Optimization & Implementation SVM Notes: Optimization & Implementation
    Rated 5.00 out of 5
    ₹399.00 Original price was: ₹399.00.₹249.00Current price is: ₹249.00.
  • spidy mail hunter software Spidy Mail Hunter: Powerful And Intelligent Website & PDF Email Extractor Software (Windows App)
    Rated 4.78 out of 5
    ₹9,999.00 Original price was: ₹9,999.00.₹599.00Current price is: ₹599.00.
  • Linear Algebra For Machine Learning And Data Science Linear Algebra For Machine Learning And Data Science
    Rated 4.71 out of 5
    ₹699.00 Original price was: ₹699.00.₹449.00Current price is: ₹449.00.
  • Javascript DOM Notes Javascript Document Object Model Notes And Codes ₹499.00

Recent Posts

Preventing Infinite Echo Loops in React Broadcast Channel

Handling Bfcache with Active WebSocket Connections in React: A Complete Tutorial

Preventing Out-of-Order Rollbacks in Optimistic UI: A Step-by-Step React Tutorial

October 2026
M T W T F S S
 1234
567891011
12131415161718
19202122232425
262728293031  
« Aug    

Neuraldemy

Neuraldemy

Neuraldemy

Software Development

Neuraldemy helps you learn ML, AI, Web Dev and data science from scratch. Addtionally, we also provide web development and hosting services for businesses, and individuals

  • Get Started
  • Contact
  • Book Services
  • Privacy Policy
  • Terms Of Use
  • Support Portal
  • Managed Hosting Terms
  • Web Development Terms
  • Refund Policy
Neuraldemy

© 2026 - A learning platform by Odist Magazine

Welcome Back!

Login to your account below

Forgotten Password? Sign Up

Create New Account!

Fill the forms below to register

*By registering into our website, you agree to the Terms & Conditions and Privacy Policy.
All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
Join Our WhatsApp Channel
  • Home
  • Tutorials
    • Machine Learning
    • Python
    • Web Development
    • Javascript
    • Mathematics
    • Deep Learning
    • Artificial Intelligence
  • Store
  • Our Services
    • Managed Hosting Services
    • Web Development Services
    • Digital Marketing Services
  • Our Softwares
    • JaggoRe AI
    • Email Extractor
    • TimeWell App
  • Login
  • Sign Up
  • Cart
Order Details

© 2026 - A learning platform by Odist Magazine

This website uses cookies. By continuing to use this website you are giving consent to cookies being used.
0