XDebug & Netbeans – Works on some projects but not on others

Wondering if anyone can explain this. I have Netbeans 23, using XDebug 3. I find that it works on some of my projects but not on others. What I mean by that is when you hit “Run” (F5) on the projects that it works in the status bar you can see “Connected” and it launches the home page of the project (localhost).

On the ones that don’t work it sits there “Waiting for connection”. I’ve checked all of the proxy settings and there’s nothing different… but clearly something is different.

Anyone had this issue and been able to work out the problem? Running PHP 8.3.14 with WAMP Server.

Flatten JSON array with multiple items – how to fit to a single csv cell?

I must get some data via API using curl and save it to a CSV file. Example JSON data set I’m getting when connected via API:

{
  "id": 24598942,
  "created_at": "2021-08-16T15:59:25.345+01:00",
  "success": true,
  "payment_amount": 90,
  "invoice_ids": [
    1646569091
  ],
  "ref_num": "2858",
  "payment_method": "BACS"
}

My PHP code:

$url = 'https://example.com/?page='.$x;

$curl = curl_init($url);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json', $auth_key_here));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$query = curl_exec($curl);
curl_close($curl);

$decoded = json_decode($data, true);
    
if ($x == 1) {
    $fp = fopen($csv, 'w');
    fputcsv($fp, array_keys($decoded[0]));
    fclose($fp);
}
    
$fp2 = fopen($csv, 'a');
    
foreach ($decoded as $fields) {
    fputcsv($fp2, array_flatten($fields));
    $row_count++;
}
fclose($fp2);

function array_flatten($nonFlat) {
    $flat = array();
    foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($nonFlat)) as $k=>$v) {
        $flat[$k] = $v;
    }
    return $flat;
}

This gives me “invoice_ids” array flatten so I can see actual data in CSV file rather than “Array” word. But some records contain multiple invoice ids in array:

{
  "id": 21016112,
  "created_at": "2020-08-17T16:00:55.552+01:00",
  "success": true,
  "payment_amount": 90,
  "invoice_ids": [
    1646540900,
    1646277088
  ],
  "ref_num": "ZN905TNVCNGQY",
  "payment_method": "Credit Card"
}

This messes up CSV file by moving some data into the wrong columns, as depicted here:
csv file

How to alter the PHP code so the flat array with multiple invoice ids fit to a single column in CSV file?

How to monitor db connections using classes/db/DbPDO.php

I am running into the problem where there are too many max_user_connections causing 500 errors. Since being on a shared server, these cannot be increased. I’m running Prestashop and I’m trying to find what method or functionality is causing this issue.

How can I monitor which routines are using classes/db/DbPDO.php?

*ERROR*     v1.7.8.11   2025/07/19 - 00:18:23: Link to database cannot be established: SQLSTATE[42000] [1203] User XXXXX already has more than 'max_user_connections' active connections at line 137 in file classes/db/DbPDO.php

I have contacted the server provider and they unfortunately cannot help me with this issue.

Windows 11: Apache, php and postgreSQL installed, phppgadmin doesn’t work [closed]

I installed Apache 2.4 via the “Apache Lounge” link (i.e. no XAMPP or WAMP), PostgreSQL 17.5 via the EDB installer, and php 8.4.10 from the php.net.

Apache and PHP work fine, i also successfully ported a database dump from ny linux system and verified that PsogreSQL is working correctly (using psql.

Apache is located in C:Apache24, php is in C:php84, and postgresql is in C:Program FilesPostgreSQL

In php.ini I have the lines

extension=pdo_pgsql
extension=pgsql

I then installed phpPgAdmin 7.13.0 from the official GitHub repository and put the extracted folders under C:Apache24htdocs.

I created a file phppgadmin.conf in C:Apache24confextra with the contents

Alias /phppgadmin "C:/Apache24/htdocs/phppgadmin"
<Directory "C:/Apache24/htdocs/phppgadmin">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

Then I added C:Apache24bin;C:php84;C:Program FilesPostgreSQL17bin to the PATH environment variable.

When i point my browser at http://localhost/phppgadmin i get a page with the contents

Your PHP installation does not support the pgsql, mbstring modules. You will need to install, enable, or compile them to use phpPgAdmin.

How can i make PHP “support the pgsql, mbstring modules”, so that i can run phppgadmin, and access my postgresql DB from php scripts?

EDIT: in C:Apache24htdocsphppgadminconfconfig.inc.php i have these lines (amongst many others):

$conf['servers'][0]['desc'] = 'PostgreSQL';
$conf['servers'][0]['host'] = 'localhost';
$conf['servers'][0]['port'] = 5432;

$conf['servers'][0]['defaultdb'] = 'postgres';
$conf['servers'][0]['username'] = 'postgres';
$conf['servers'][0]['password'] = 'MyPGPassword';

How to create a custom Gutenberg block with a dynamic render callback in WordPress?

I’m trying to create a custom Gutenberg block in WordPress that renders its content dynamically on the server side using PHP.

I’ve registered the block using register_block_type, and added a render_callback function, but nothing appears in the editor or frontend.

function myplugin_register_block() {
    register_block_type( __DIR__ . '/my-block', array(
        'render_callback' => 'myplugin_render_my_block',
    ));
}
add_action( 'init', 'myplugin_register_block' );

function myplugin_render_my_block( $attributes ) {
    return '<div class="dynamic-block">Hello, this is dynamic content!</div>';
}

import { registerBlockType } from '@wordpress/blocks';

registerBlockType('myplugin/dynamic-block', {
    edit: () => {
        return <p>Hello from the editor!</p>;
    },
    save: () => {
        return null; // Dynamic rendering
    },
});

What I’ve tried:
Checked if the function name is correct

Flushed permalinks

Ensured that the PHP file is loaded properly

Confirmed that block assets (JS/CSS) load correctly

What I expect:
When I insert the block in the editor, I want to see:

A preview (even just a placeholder),

And when I view the post on the frontend, the HTML returned from render_callback().

Allowed propertyName for Javascript setProperty

I’m using javaScript setProperty(propertyName, value) a lot and it works perfectly until I want to set properties like justifyContent when using flexboxes.

It does work with the propertyName ‘flexDirection’ – I can change from row to column or vice verse.

Now when I want to set the ‘justifyContent’ propertyName to ‘flex-start’, ‘center’, ‘left’ it looks like it just doesn’t execute it (or ignores it). It works of course when specifically use something like object.style.justifyContent = “flex-end”

I have a feeling that there is a restricted list of propertyNames for which one can use the setProperty. I tried to look for a list of allowed propertyNames but couldn’t find it anywhere which makes me doubt that this is the problem (it can’t be that I’m the first one to question this).

Also possible that the naming convention flexDirection or justifyContent is different.

Biggest chance is that I’m overseeing something stupid because of my lack of experience:D.

Can anyone point me in the right direction on where to find answer to this mystery?

How to dynamically compile a directory of mdx files before each vite build?

I’m using React and Vite for a project I’m working on.

What I currently need to do is compile and render all of the MDX files in a directory but I don’t want to do the actual compilation on the client side and I want to exclude some of the files so that they aren’t even in the final build.

I already tried using Vite’s glob importing but I don’t think that can be used to filter out files based on metadata.

My current plan is to:

  1. Read all of my mdx files into an array
  2. Parse the frontmatter and decide which ones I want to keep accordingly
  3. Run the remaining files through the mdx compiler
  4. Be able to import the array into my app

I have all of it figured out except the last step, but I keep encountering errors.

From my research it appears vite offers both virtual modules and build hooks for executing code before the build.

I currently have tried using virtual modules and it seems like the right solution but I’m just not sure how to get my mdx data exported from a virtual module.

import fs from "fs"
import fm from "front-matter"
import { evaluate } from "@mdx-js/mdx"
import * as runtime from "react/jsx-runtime"
import remarkFrontmatter from "remark-frontmatter"
import remarkMdxFrontmatter from "remark-mdx-frontmatter"
import toSource from "tosource"
const path = "./src/posts"
const rawPosts = fs.readdirSync(path).map(fileName => {
  const file = fs.readFileSync(`${path}/${fileName}`)
  return String(file)
})
const filteredPosts = rawPosts.filter(async file => {
  const frontmatter = fm(file)
  // use frontmatter to decide which files to keep
  return true
})
const finalPosts = await Promise.all(
  filteredPosts.map(async file => {
    const parsed = await evaluate(file, {
      ...runtime,
      remarkPlugins: [
        remarkFrontmatter,
        remarkMdxFrontmatter,
      ],
    })
    return parsed
  })
)
export default function posts() {
  const moduleId = "virtual:posts"
  const resolvedModuleId = "" + moduleId

  return {
    name: "my-plugin",
    resolveId(id) {
      if (id === moduleId) {
        return resolvedModuleId
      }
    },
    load(id) {
      if (id === resolvedModuleId) {
        // not sure what to do here
        return ?
      }
    },
  }
}

I already tried JSON.stringifying the array but the function disappears when doing that, so what is a better approach? Should I just be compiling the mdx and not evaluating it yet? In that case I don’t know how I would dynamically create multiple virtual modules to import. I realize I could probably use the buildStart hook to run a separate script but I feel like it could be achieved with virtual modules similar to how I have tried so far.

Blogger homepage sections are empty after adding VideoObject JSON-LD schema to some posts

I’m facing an issue with my Blogger website where the sections on the homepage are not displaying any posts. I suspect this started happening after I edited some of my posts to add a VideoObject JSON-LD schema

The Problem:

The sections for “مقالات متنوعة” (Miscellaneous Posts), “ويندوز” (Windows), and “لينكس” (Linux) on my homepage are empty. They only show the section title, but no posts appear underneath, even though they have the correct labels.

enter image description here

Recent Changes:

I believe the issue started after I tried to fix a video indexing problem reported in Google Search Console. I edited several posts that have embaded youtube viedo’s, and added the following snippet to the post body using the HTML view in the editor

enter image description here

example:


<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "VideoObject",
  "name": "how to share files in the Linux system",
  "description": "Some description.",
  "thumbnailUrl": "https://i.ytimg.com/vi/zNxLmpk0lgM/maxresdefault.jpg",
  "uploadDate": "2017-08-04T14:29:00+03:00",
  "embedUrl": "https://www.youtube.com/embed/zNxLmpk0lgM",
  "contentUrl": "https://www.pro-cs-is.com/2017/08/linux-file-sharing.html"
}
</script>

and this

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "VideoObject",
  "name": "how to make a backup copy of Windows without programs and restore it",
  "description": "Some descrption.",
  "thumbnailUrl": "https://i.ytimg.com/vi/C7BM3rrPg0o/maxresdefault.jpg",
  "uploadDate": "2018-01-24T04:38:00+03:00",
  "embedUrl": "https://www.youtube.com/embed/C7BM3rrPg0o",
  "contentUrl": "https://www.pro-cs-is.com/2018/01/windows-backup-restore.html"
}
</script>

I removed this code from posts and the problem is still present

Asking for an opinion on a specifc JavaScript book

I have tried several books on JavaScript programming and have been dissatisfied with them all. I would like some advice on a specific book I have found but I don’t know if it would still be relavant today with modern JavaScript.
Is the book “Secrets of the JavaScript Ninja Second Edition” still relavant? If not, what book would you suggest in its place?

Intersection Observer for two elements

I started off with a situation where I wanted to observe an element on my page, let’s call it “trigger”. It is usually always visible upon page load (above the fold).

As we scroll down, if “trigger” is no longer visible, I want to apply a CSS class to another element.

Here is my code for doing so and it works:

const trigger = document.getElementById('trigger')
const otherElement = document.getElementById('some-other-element')

const intersectionObserver = new IntersectionObserver(
    ([entry]) => {
        if (entry.intersectionRatio === 0) {
            otherElement.classList.add('special-class')
        } else if (entry.intersectionRatio === 1) {
            otherElement.classList.remove('special-class')
        }
    },
    {
        threshold: [0, 1],
    },
)

intersectionObserver.observe(trigger)

Upon testing this, I realized that users can have smaller devices in which case the bottom edge of the device is actually above where the “trigger” sits so on page load, the “trigger” is actually not visible. Therefore, my “special-class” is automatically applied which is not what I want to have happen.

I thought of a solution to this issue which would be to observe two elements: my trigger and another element like a nav bar or a header which will always be at the top of the page and only if both elements have been scrolled past do I apply my “special-class”. However, I could not figure out how to get IntersectionObserver to observe both elements and how my code would change accordingly.

How to fix Post validation failed: title: Cast to string failed – formidable (Node)

I’m making a POST request to create a blog post and I’m using formidable to upload an img for each post but I’m receiving a 400 error.

UPDATE: I’m now receiving an actual error:

Post validation failed: title: Cast to string failed for value "[ 'njk' ]" (type Array) at path "title", body: Cast to string failed for value "[ 'ugyjhvbhujb' ]" (type Array) at path "body", slug: Path `slug` is required.

console.log(formData) returns an object of strings in the frontend like:

{ title: "suyghb", body: "cguyasjbhxz"...}

but in the backend the values in fields aka formData are now arrays like:

{ body: [ 'ihubj' ], title: [ '87tugyh' ] }

why is this?

​​I tried setting

'Content-Type': 'application/json

but then I receive the error request entity too large.

posts.js (controllers)

export const create = (req, res) => {
    let form = new formidable.IncomingForm()
    form.keepExtensions = true
    form.parse(req, (err, fields, files) => {
        if (err) {
            return res.status(400).json({
                error: "Image could not be uploaded."
            })
        }
      
        let post = new Post(fields)
        if (files.photo) {
            const photoFilePath = files.photo.map((file) => {
                return file.filepath
            })
            }
            const photoStr = photoFilePath.toString()
            post.photo.data = fs.readFileSync(photoStr, 'utf-8')
            post.photo.contentType = files.photo.type
        }
        post.save()
           /• .then & .catch •/

Form Validation

const { body, validationResult } = require('express-validator');

exports.userSignupValidator = (req, res, next) => {
    validationBodyRules = [
        body('email', 'email is required').exists(),
        body('password', 'password is required').exists(),
        body('email', 'email is required').notEmpty(),
        body('password', 'password is required').notEmpty()
    ]

    checkRules = (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        next();
    };
}

API request (client side)

export const createPost = (token, formData) => {
    console.log(formData)
    return fetch('http://localhost:8000/posts/new-post', {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${token}`,
        },
        body: formData
    })
        .then(response => {
            return response.json();
        })
        .catch(err => {
            console.error(err);
        });
};

Strange behaviour of NumberInput and Select component in ChakraUI

I am facing a strange situation with ChakraUI components. I am trying to use ChakraUI’s NumberInput component and Select component.

Scenario 1:
If the NumberInput component is written before the Select component and I try to enter any value in it, I am able to enter the value but
if I try to select any value in the Select component, the error below is thrown:

TypeError: selectEl.options is not iterable
    at syncSelectElement (http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:37436:39)
    at action (http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:5214:29)
    at http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:36885:7
    at http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:5109:21
    at react-stack-bottom-frame (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5690b4d1:17478:20)
    at runWithFiberInDEV (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5690b4d1:1485:72)
    at commitHookEffectListMount (http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:8460:122)
    at commitHookPassiveMountEffects (http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:8518:60)
    at commitPassiveMountOnFiber (http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:9887:29)
    at recursivelyTraversePassiveMountEffects (http://localhost:5173/node_modules/.vite/deps/@chakra-ui_react.js?v=5690b4d1:9868:13)

Scenario 2:
When the Select component is used before the NumberInput component, it works fine, but I am unable to enter any value in the NumberInput component. In this case also, no errors are thrown.

I inspected the HTML and found that both components have the same id attribute, which is most likely the source of the issue. However, I tried giving them different ids, but the id attribute is not updating and is being rendered as some ChakraUI-generated id value.

Versions:

  • ChakraUI: ^3.21.0
  • tailwindcss: ^4.1.10
  • vite: ^6.3.5
  • react: ^19.1.0

I have encountered this situation for the first time while using ChakraUI. Kindly help me in resolving the issue.

Please let me know if additional information is required.

How to delete Firestore document when a user closes the web page

I have a web application utilising React and Firestore where users can create “rooms” which when active allow users to interact with the host. There is an option to both activate and deactivate the room to disallow interaction.

I am considering what is the best way to automatically “deactivate the room” – either when the user closes the web app (either on desktop or mobile), or if the room has been inactive for a while; something around an hour.

I have researched this and found two possibilities:

  1. Firestore – TTL – it seems like this is the most reliable way, where I could set a field to have the room’s expiry an hour in the future each time it is updated. I wanted to ask if there are any costs/limits to using TTL, as it seems the only cost is that the document delete will count against your quota.
  2. To use visibilityState / unload of which they seem to have their caveats on mobile, e.g.
const onVisibilityChange = () => {
  if (document.visibilityState === 'hidden') {
    delDoc();
  }
};

useLayoutEffect(() => {
  document.addEventListener("unload", onVisibilityChange);
  return () => document.removeEventListener("unload", onVisibilityChange);
});

For desktop: I am interested to know if the user has closed the tab or navigated away from the room hosting page.
For mobile: I am interested to know if the user has closed the tab via the browser, or if the user has force closed the browser via the app switcher.

Any help would be much appreciated.

Bootstrap 5 mega menu not displaying properly on dekstop

I have been bashing my head against a brick wall all afternoon and I am stumped.

I want to create a Bootstrap 5 mega menu and I’m not using jQuery so have all the JavaScript as Vanilla JavaScript.

On mobile, everything is working as it should but on desktop, when I click on the Services menu option, the mega menu only displays to the width of the parent width so all the text is shrunk.

What am I doing wrong or missing?

Screenshot showing the expanded mega menu on desktop view

Here is my complete code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Mega Menu with Slide-in Mobile Nav</title>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
  <style>
    body {
      margin: 0;
      font-family: Arial, sans-serif;
    }

    .site-menu {
      list-style: none;
      padding-left: 0;
      margin: 0;
    }

    .site-menu > li {
      display: inline-block;
      position: relative;
      margin-right: 20px;
    }

    .site-menu > li > a {
      color: white;
      text-decoration: none;
      padding: 8px 12px;
      display: inline-block;
    }

    .site-menu > li > a:hover {
      color: #ffc107;
    }

    .site-navigation {
  position: relative;
  background: #333;
  padding: 1rem;
  max-width: 1200px;   /* Set this to your navbar max width */
  margin: 0 auto;      /* center navbar */
}

.site-navigation .container-fluid {
  display: none;
  position: absolute;
  background: #fff;
  top: 100%;           /* just below navbar */
  left: 0;             /* align left edge of navbar */
  width: 100%;         /* full width of navbar */
  z-index: 999;
  padding: 20px 0;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  box-sizing: border-box;
  transform: none !important;  /* cancel any shifts */
}

.site-navigation li.show > .container-fluid {
  display: block;
}


    /* Mobile Menu */
    .site-mobile-menu {
      width: 300px;
      position: fixed;
      top: 0;
      right: 0;
      height: 100vh;
      background: white;
      transform: translateX(110%);
      transition: transform 0.3s ease-in-out;
      z-index: 1050;
      overflow-y: auto;
      box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
      display: flex;
      flex-direction: column;
    }

    .site-mobile-menu.open {
      transform: translateX(0);
    }

    .site-menu-toggle {
      cursor: pointer;
      display: inline-block;
    }

    /* Mobile submenu */
    .mobile-mega-menu {
      overflow: hidden;
      max-height: 0;
      transition: max-height 0.4s ease;
    }

    .mobile-mega-menu.show {
      max-height: 800px;
    }

    .mobile-mega-content {
      padding: 1rem;
      background: #f8f9fa;
    }

    .mobile-mega-content .list-group-item {
      font-size: 0.95rem;
    }

    /* Click-blocker for closing menu */
    .menu-overlay {
      display: none;
      position: fixed;
      inset: 0;
      z-index: 1040;
      background: rgba(0, 0, 0, 0.3);
    }

    .menu-overlay.active {
      display: block;
    }

    /* Close icon styling */
    #js-menu-close {
      font-size: 2rem;
      font-weight: bold;
      line-height: 1;
      cursor: pointer;
      user-select: none;
    }

    /* Mobile menu list styling */
    .site-mobile-menu .site-menu {
      padding-left: 0;
      margin: 0;
      list-style: none;
      flex-grow: 1;
    }

    .site-mobile-menu .site-menu > li {
      border-bottom: 1px solid #ddd;
      width: 100%;
    }

    .site-mobile-menu .site-menu > li > a {
      display: block;
      padding: 12px 20px;
      color: #333;
      text-decoration: none;
      font-weight: 600;
    }

    .site-mobile-menu .site-menu > li > a:hover {
      background: #f0f0f0;
    }
  </style>
</head>
<body>

  <div class="menu-overlay" id="menuOverlay"></div>

  <div class="container">
    <nav class="site-navigation text-end" role="navigation">
      <div class="d-inline-block d-lg-none py-3">
        <a href="#" id="js-menu-toggle" class="site-menu-toggle text-white">
          <span class="icon-menu h3">☰</span>
        </a>
      </div>
      <ul class="site-menu js-clone-nav d-none d-lg-block">
        <li class="nav-item"><a class="nav-link" href="#">Home</a></li>
        <li class="nav-item" id="desktopServices">
          <a href="#" class="nav-link">Services ▾</a>
          <div class="container-fluid">
            <div class="row my-4">
              <div class="col-md-6 col-lg-4 mb-3">
                <div class="list-group list-group-flush">
                  <p class="mb-0 list-group-item text-uppercase fw-bold">Celebrations of Love</p>
                  <a href="#" class="list-group-item">Wedding & Civil Ceremonies</a>
                  <a href="#" class="list-group-item">Engagement Parties</a>
                  <a href="#" class="list-group-item">Anniversaries</a>
                </div>
              </div>
              <div class="col-md-6 col-lg-4 mb-3">
                <div class="list-group list-group-flush">
                  <p class="mb-0 list-group-item text-uppercase fw-bold">Birthday Celebrations</p>
                  <a href="#" class="list-group-item">Adult Birthday Celebrations</a>
                  <a href="#" class="list-group-item">Teen Birthday Parties</a>
                  <a href="#" class="list-group-item">Child Birthday Parties</a>
                </div>
              </div>
              <div class="col-md-6 col-lg-4 mb-3">
                <div class="list-group list-group-flush">
                  <p class="mb-0 list-group-item text-uppercase fw-bold">Special Occasions</p>
                  <a href="#" class="list-group-item">Themed Parties (e.g. Halloween)</a>
                  <a href="#" class="list-group-item">School/College Discos</a>
                  <a href="#" class="list-group-item">Proms</a>
                  <a href="#" class="list-group-item">Corporate Events</a>
                </div>
              </div>
            </div>
          </div>
        </li>
        <li class="nav-item"><a class="nav-link" href="#">Contact</a></li>
        <li class="nav-item"><a class="nav-link" href="#">Hire</a></li>
        <li class="nav-item"><a class="nav-link" href="#">Blog</a></li>
        <li class="nav-item"><a class="nav-link" href="#">About</a></li>
      </ul>
    </nav>

    <div class="site-mobile-menu" id="mobileMenu" aria-hidden="true">
      <div class="p-3 border-bottom text-end">
        <span id="js-menu-close" aria-label="Close menu" role="button" tabindex="0">&times;</span>
      </div>
      <ul class="site-menu">
        <li><a href="#">HOME</a></li>
        <li>
          <a href="#" id="mobileServicesToggle" aria-expanded="false" aria-controls="mobileMegaMenu">SERVICES ▾</a>
          <div class="mobile-mega-menu" id="mobileMegaMenu" aria-hidden="true">
            <div class="mobile-mega-content">
              <div class="list-group mb-3">
                <p class="fw-bold text-uppercase mb-0">Celebrations of Love</p>
                <a href="#" class="list-group-item">Wedding & Civil Ceremonies</a>
                <a href="#" class="list-group-item">Engagement Parties</a>
                <a href="#" class="list-group-item">Anniversaries</a>
              </div>
              <div class="list-group mb-3">
                <p class="fw-bold text-uppercase mb-0">Birthday Celebrations</p>
                <a href="#" class="list-group-item">Adult Birthday Celebrations</a>
                <a href="#" class="list-group-item">Teen Birthday Parties</a>
                <a href="#" class="list-group-item">Child Birthday Parties</a>
              </div>
              <div class="list-group">
                <p class="fw-bold text-uppercase mb-0">Special Occasions</p>
                <a href="#" class="list-group-item">Themed Parties</a>
                <a href="#" class="list-group-item">School/College Discos</a>
                <a href="#" class="list-group-item">Proms</a>
                <a href="#" class="list-group-item">Corporate Events</a>
              </div>
            </div>
          </div>
        </li>
        <li><a href="#">BLOG</a></li>
        <li><a href="#">ABOUT</a></li>
        <li><a href="#">CONTACT</a></li>
      </ul>
    </div>
  </div>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
  const menuToggle = document.getElementById('js-menu-toggle');
  const mobileMenu = document.getElementById('mobileMenu');
  const menuClose = document.getElementById('js-menu-close');
  const menuOverlay = document.getElementById('menuOverlay');
  const mobileServicesToggle = document.getElementById('mobileServicesToggle');
  const mobileMegaMenu = document.getElementById('mobileMegaMenu');
  const desktopServices = document.getElementById('desktopServices');

  // Open mobile menu
  menuToggle.addEventListener('click', e => {
    e.preventDefault();
    mobileMenu.classList.add('open');
    menuOverlay.classList.add('active');
    mobileMenu.setAttribute('aria-hidden', 'false');
  });

  // Close mobile menu (close icon)
  menuClose.addEventListener('click', () => {
    closeMobileMenu();
  });

  // Close mobile menu (overlay click)
  menuOverlay.addEventListener('click', () => {
    closeMobileMenu();
  });

  // Close mobile menu helper
  function closeMobileMenu() {
    mobileMenu.classList.remove('open');
    menuOverlay.classList.remove('active');
    mobileMenu.setAttribute('aria-hidden', 'true');

    // Also close the Services submenu if open
    mobileMegaMenu.classList.remove('show');
    mobileServicesToggle.setAttribute('aria-expanded', 'false');
    mobileMegaMenu.setAttribute('aria-hidden', 'true');
  }

  // Toggle Services submenu on mobile
  mobileServicesToggle.addEventListener('click', e => {
    e.preventDefault();
    const isExpanded = mobileServicesToggle.getAttribute('aria-expanded') === 'true';
    if (isExpanded) {
      mobileMegaMenu.classList.remove('show');
      mobileServicesToggle.setAttribute('aria-expanded', 'false');
      mobileMegaMenu.setAttribute('aria-hidden', 'true');
    } else {
      mobileMegaMenu.classList.add('show');
      mobileServicesToggle.setAttribute('aria-expanded', 'true');
      mobileMegaMenu.setAttribute('aria-hidden', 'false');
    }
  });

  // Close mobile menu when clicking on any mobile link except Services toggle
  mobileMenu.querySelectorAll('a').forEach(link => {
    link.addEventListener('click', e => {
      // Don't close if clicking the Services toggle link itself
      if (link === mobileServicesToggle) return;
      closeMobileMenu();
    });
  });

  // Desktop Services menu toggle on click
const desktopServicesLink = desktopServices.querySelector('a');

desktopServicesLink.addEventListener('click', (e) => {
  e.preventDefault();
  desktopServices.classList.toggle('show');
});

// Optional: close mega menu if clicking outside
document.addEventListener('click', (e) => {
  if (!desktopServices.contains(e.target)) {
    desktopServices.classList.remove('show');
  }
});
});

  </script>
  </body>
  </html>

How to Prevent Suspense Waterfall in React 18 with Dynamic, Server-Driven Components?

I’m building a React 18.3 (TypeScript) app where components are dynamically rendered based on a server-provided JSON structure. I’m using React.Suspense and React.lazy for code-splitting to load components dynamically. Here’s a simplified example:

import React, { Suspense, lazy } from 'react';

// Server response
const serverConfig = {
  type: 'Layout',
  children: [
    { type: 'WidgetA', dataUrl: '/api/widgetA' },
    { type: 'WidgetB', dataUrl: '/api/widgetB' },
  ],
};

// Component map
const componentMap = {
  Layout: lazy(() => import('./components/Layout')),
  WidgetA: lazy(() => import('./components/WidgetA')),
  WidgetB: lazy(() => import('./components/WidgetB')),
};

const renderTree = (config) => {
  const Component = componentMap[config.type];
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Component {...config}>
        {config.children?.map(renderTree)}
      </Component>
    </Suspense>
  );
};

function App() {
  return renderTree(serverConfig);
}

Each component fetches its own data using a custom hook with fetch. My issue is that nested Suspense boundaries cause a waterfall effect, where components load sequentially, slowing down the UI. I’ve tried useTransition, but it doesn’t fully resolve the issue for dynamic trees.

How can I parallelize data fetching for all components to avoid the Suspense waterfall?
Are there best practices or libraries (e.g., React Query) for optimizing dynamic component loading with server-driven configs?

I’ve read the React docs on Suspense and Concurrent Rendering but couldn’t find guidance on dynamic, server-driven setups. Any code examples or patterns would be helpful!