≡ Menu

If you’ve built a React application using Server-Side Rendering (SSR), you’ve inevitably run into the frustrating Hydration Mismatch error.

This often happens when your component needs to render something that relies on browser-specific objects like window or localStorage.

The error occurs because the server, which renders your initial HTML, has no idea what window.innerWidth is.

When the client-side JavaScript loads and tries to “hydrate” (or attach interactivity to) the server-rendered HTML, it finds a structural difference in the DOM and throws a fit, often wiping the screen and starting over.

This defeats the purpose of SSR!

Fortunately, the fix is elegant and simple: defer all client-specific logic until after hydration is complete.


The Code Fix: useState and useEffect as Gatekeepers

 

This component structure uses a clever combination of the useState and useEffect hooks to safely access the browser environment without causing a server-client mismatch.

import React, { useState, useEffect } from 'react';

function LayoutCheckFixed() {
  // 1. Initial State: The hydration "gate"
  const [isMobile, setIsMobile] = useState(false); 
  const [hasMounted, setHasMounted] = useState(false); 

  // 2. useEffect: The client-only trigger
  useEffect(() => {
    // ONLY runs on the client, after the initial render.
    const checkIsMobile = window.innerWidth < 768;
    setIsMobile(checkIsMobile);
    setHasMounted(true); // Open the gate!
  }, []); 

  // 3. Conditional Render: The Hydration "Placeholder"
  if (!hasMounted) {
    return null// Server and initial client render must match this!
  }

  // 4. Final Render: The stable, client-specific output
  if (isMobile) {
    return <div>Mobile Layout</div>;
  } else {
    return <div>Desktop Layout</div>;
  }
}

🚀 How the Hydration-Safe Component is Processed

 

Understanding the sequence is key to mastering SSR. The code works because the server ignores useEffect, and the client’s initial render output matches the server’s output perfectly.

Phase 1: Server-Side Rendering (SSR) 🖥

 

  1. State Initialization: isMobile is false, and hasMounted is false.

  2. useEffect Ignored: The server skips the entire useEffect block; it never tries to access window.innerWidth.

  3. Conditional Check (Step 3): Since hasMounted is false, the component immediately returns null.

  4. HTML Output: The server sends HTML containing an empty space (the result of rendering null) for this component. This is stable and safe.

Phase 2: Client-Side Hydration 🌐

 

  1. Client Initial Render: The client-side React code runs. It finds the same initial state (hasMounted is false and also returns null.

  2. Hydration Success: React compares the client’s null with the server’s null. They match perfectly! Hydration is successful, and the app becomes interactive.

  3. useEffect Execution: Only now does the useEffect hook run.

    • It safely checks the real window.innerWidth.

    • It sets the correct isMobile state.

    • Crucially, it sets setHasMounted to true.

  4. Final Re-Render: The state change triggers a final, correct re-render. The component skips the return null check and renders the final, stable, client-specific output (Mobile Layout or Desktop Layout).

By delaying the logic that causes the mismatch, you ensure a smooth, single hydration pass, preserving the performance benefits of Server-Side Rendering. Happy hydrating!

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }
add to cart feature in javascript

Add to cart ellipse shape red button icon vector. Shopping cart in white background icon.

Adding items to a shopping cart is a foundational feature of any e-commerce site.

While large frameworks like React or Vue handle this beautifully, understanding the core logic with plain JavaScript is crucial.

In this tutorial, we will build a working, in-memory cart using just HTML, CSS (for basic styling), and JavaScript.

🚀 The Three Core Steps

 

The entire process boils down to these three steps:

  1. HTML Setup: Create the product display and the cart counter.

  2. Data Management (JavaScript): Use an array to store and manage cart items (the data structure).

  3. UI Update (JavaScript): Update the on-screen numbers and details based on the stored data.


Step 1: Setting up the HTML Structure

 

We need a product to click and a visible counter for the cart. Notice how we use data-attributes on the button to store the product information directly. This is a clean way to pass data from the HTML element to our JavaScript function.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS Cart Tutorial</title>
</head>
<body>

    <h1>Simple E-commerce Example</h1>

    <div id="product-1" style="border: 1px solid #ccc; padding: 15px; margin-bottom: 20px;">
        <h2>The JavaScript Handbook</h2>
        <p>A comprehensive guide to modern JS development.</p>
        <p>Price: <strong>$10.00</strong></p>
        
        <button class="add-to-cart" 
                data-product-id="JSH001" 
                data-product-name="The JavaScript Handbook" 
                data-product-price="10.00">
            Add to Cart
        </button>
    </div>

    <hr>

    <div id="cart-summary">
        <h2>🛍️ Shopping Cart Summary</h2>
        <p>Items in Cart: <strong id="cart-count">0</strong></p>
    </div>

    <script src="cart.js"></script> 
</body>
</html>

Step 2: Initializing the Cart and DOM Elements (cart.js)

In our JavaScript file (cart.js), we first set up the variables we’ll need to work with.

// cart.js

/**
 * 1. The Data Structure
 * This array will hold objects representing items in our cart.
 * Example item structure: { id: "JSH001", name: "...", price: 10.00, quantity: 1 }
 */
let cart = []; 

// 2. DOM Elements
const cartCountElement = document.getElementById('cart-count');
const addToCartButtons = document.querySelectorAll('.add-to-cart');

Step 3: Creating the Core Logic Functions

 

We need two main functions: one to update the cart data and one to reflect those changes on the screen.

Function 1: updateCartUI()

This function calculates the total number of items currently in the cart and updates the display.

// Recalculates the total item count and updates the display
function updateCartUI() {
  // Use the .reduce() method to sum the 'quantity' property of every item in the cart array
  const totalItems = cart.reduce((total, item) => total + item.quantity, 0);
  
  cartCountElement.textContent = totalItems;
  
  // Optional: Log the cart for easy debugging in the console
  console.log('Current Cart State:', cart);
}

Function 2: addItemToCart()

This is the central function. It checks if an item is already in the cart. If it is, we just increment the quantity. If it’s new, we add a new item object to the array.

// Handles adding a new item or increasing the quantity of an existing one
function addItemToCart(productId, name, price) {
  // Check if item already exists by searching its ID
  const existingItemIndex = cart.findIndex(item => item.id === productId);

  if (existingItemIndex > -1) {
    // 1. Item exists: Increment its quantity
    cart[existingItemIndex].quantity += 1;
  } else {
    // 2. Item is new: Add a new object to the cart array
    cart.push({
      id: productId,
      name: name,
      price: parseFloat(price), // Important: Convert string price to a number
      quantity: 1
    });
  }

  // After every change to the data, update the display!
  updateCartUI();
}

Step 4: Attaching the Event Listener

The final step is to listen for clicks on our buttons and call the addItemToCart function, pulling the product data from the button’s data-attributes.

// Loop through all buttons and attach a click event listener
addToCartButtons.forEach(button => {
  button.addEventListener('click', () => {
    // Extract data from the clicked button
    const productId = button.dataset.productId;
    const productName = button.dataset.productName;
    const productPrice = button.dataset.productPrice;

    // Call the core logic
    addItemToCart(productId, productName, productPrice);
  });
});

// Run this once when the script loads to initialize the count to '0'
updateCartUI();

🎉 Conclusion and Next Steps

You now have a functional “Add to Cart” button! Click it, and you’ll see the count update instantly.

🌟 Challenge: Making the Cart Persistent

Currently, if you refresh the page, the cart array resets to empty.

In a real-world application, you need the cart to persist.

Your next step should be integrating localStorage to save the cart array data between page loads.

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }