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

react-mui-menu-button

v1.0.4

Published

Menu recursivo con N niveles para React con Material-UI

Downloads

470

Readme

React Menu Button MUI

npm version

Menú desplegable recursivo con soporte para múltiples niveles, navegación, scroll suave y total personalización. Construido sobre Material UI y React Router.


Componente para su uso en dos temas

| Modo claro | Modo oscuro | | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | Menú blanco | Menú negro |


📦 Instalación

npm install react-mui-menu-button
# o
yarn add react-mui-menu-button
# o
pnpm add react-mui-menu-button

Dependencias requeridas

Este paquete espera que tu proyecto ya tenga instalados:

  • @mui/material
  • @emotion/react
  • @emotion/styled
  • react-router-dom

🚀 Uso básico

import { DropdownWithSubmenu } from "react-mui-menu-button";
import { menuItems } from "./menuData"; // tu array de MenuItem

function App() {
  return <DropdownWithSubmenu menuItems={menuItems} triggerText="Explorar" />;
}

📘 Estructura de datos (MenuItem)

Cada ítem del menú sigue la interfaz:

interface MenuItem {
  label: string; // Texto visible
  href?: string; // Ruta de navegación (React Router)
  hash?: string; // Ancla para scroll interno (sin #)
  icon: React.ReactNode; // Icono (elemento JSX)
  children?: MenuItem[]; // Subítems anidados
}
  • ✅ Puedes combinar href y hash.
  • ✅ Si solo usas hash, se tomará el href del ancestro más cercano como base.
  • ✅ Ítems sin href/hash y sin hijos → no hacen nada al hacer clic.
  • ✅ Ítems sin href/hash pero con hijos → expanden/colapsan el submenú.

🧱 Ejemplo rápido de estructura menuItems

import type { MenuItem } from "react-mui-menu-button";
import {
  Code as CodeIcon,
  GitHub as GitHubIcon,
  Article as ArticleIcon,
  Business as BusinessIcon,
  Person as PersonIcon,
} from "@mui/icons-material";

export const menuItems: MenuItem[] = [
  {
    label: "Portafolio Técnico",
    href: "/portfolio",
    icon: <CodeIcon />,
    children: [
      {
        label: "Proyectos Open Source",
        hash: "open-source",
        icon: <GitHubIcon />,
        children: [
          {
            label: "React Components",
            hash: "react-components",
            icon: <CodeIcon />,
            children: [
              {
                label: "Componentes Avanzados",
                hash: "advanced-components",
                icon: <CodeIcon />,
              },
              {
                label: "Hooks Personalizados",
                hash: "custom-hooks",
                icon: <CodeIcon />,
              },
            ],
          },
          {
            label: "Node.js Libraries",
            hash: "node-libraries",
            icon: <CodeIcon />,
          },
          {
            label: "UI Kits",
            hash: "ui-kits",
            icon: <CodeIcon />,
          },
        ],
      },
      {
        label: "Demos Interactivas",
        hash: "demos",
        icon: <CodeIcon />,
        children: [
          {
            label: "Animaciones Web",
            hash: "web-animations",
            icon: <CodeIcon />,
          },
          {
            label: "APIs REST",
            hash: "rest-apis",
            icon: <CodeIcon />,
          },
        ],
      },
      {
        label: "Casos de Estudio",
        hash: "case-studies",
        icon: <ArticleIcon />,
      },
      {
        label: "Repositorios GitHub",
        hash: "repositories",
        icon: <GitHubIcon />,
      },
    ],
  },
  {
    label: "Emprendimientos",
    href: "/ventures",
    icon: <BusinessIcon />,
    children: [
      {
        label: "Puertas & Portones",
        hash: "doors-gates",
        icon: <BusinessIcon />,
        children: [
          {
            label: "Modelos Estándar",
            hash: "standard-models",
            icon: <BusinessIcon />,
          },
          {
            label: "Medidas Personalizadas",
            hash: "custom-measures",
            icon: <BusinessIcon />,
          },
        ],
      },
      {
        label: "Catálogo de Productos",
        hash: "catalog",
        icon: <BusinessIcon />,
      },
      {
        label: "Diseños Personalizados",
        hash: "designs",
        icon: <BusinessIcon />,
      },
    ],
  },
  {
    label: "Blog",
    href: "/blog",
    icon: <ArticleIcon />,
  },
  {
    label: "Perfil",
    href: "/",
    icon: <PersonIcon />,
    children: [
      {
        label: "Stack Tecnológico",
        hash: "technology-stack",
        icon: <CodeIcon />,
      },
      {
        label: "Experiencia Laboral",
        hash: "experience",
        icon: <PersonIcon />,
      },
    ],
  },
];

🧩 Props principales

1. Datos y contenido

| Prop | Tipo | Default | Descripción | | ----------- | ------------------------- | ---------------- | ------------------------------------- | | menuItems | MenuItem[] | defaultMenuItems | Array de ítems del menú | | loading | boolean | false | Muestra estado de carga | | loadingText | string | "Cargando..." | Texto del spinner | | emptyState | React.ReactNode | undefined | Componente cuando no hay ítems | | triggerText | string | React.ReactNode | "EXPLORAR" | Texto o elemento del botón disparador | | triggerIcon | React.ReactNode | undefined | Icono que acompaña al texto del botón |

2. Comportamiento

| Prop | Tipo | Default | Descripción | | ------------------- | -------------------- | ------- | --------------------------------- | | onItemClick | (item, path) => void | - | Callback al hacer clic en un ítem | | onHoverItem | (item, path) => void | - | Callback al pasar el mouse | | onOpen | () => void | - | Al abrir el menú | | onClose | () => void | - | Al cerrar el menú | | maxDepth | number | 5 | Profundidad máxima | | openOnHover | boolean | true | Abrir con hover | | closeOnClickOutside | boolean | true | Cerrar al hacer clic fuera | | closeDelay | number (ms) | 300 | Retraso para cerrar |

3. Estilos y diseño

| Prop | Tipo | Default | Descripción | | ------------ | ---------------------------------- | -------- | ---------------------- | | sx | SxProps | - | Estilos del contenedor | | paperSx | SxProps | - | Estilos del Paper | | itemSx | SxProps | - | Estilos del ítem | | submenuSx | SxProps | - | Estilos del submenú | | density | compact | standard | comfortable | standard | Densidad vertical | | showDividers | boolean | false | Mostrar separadores | | dividerColor | string | divider | Color del divisor |

4. Iconos

| Prop | Tipo | Default | Descripción | | ------------ | -------------------- | --------------- | ------------------ | | expandIcon | React.ReactNode | ChevronRight | Submenú cerrado | | collapseIcon | React.ReactNode | ArrowDropUpIcon | Submenú abierto | | submenuIcon | React.ReactNode | expandIcon | Icono del submenú | | iconPosition | start | end | none | end | Posición del icono |

5. Comportamiento avanzado

| Prop | Tipo | Default | Descripción | | --------------- | -------------- | ------- | ------------------- | | open | boolean | - | Control externo | | defaultOpen | boolean | false | Estado inicial | | initialDepth | number | 0 | Profundidad inicial | | autoExpandDepth | number | 0 | Auto expansión | | scrollIntoView | boolean | true | Scroll por hash | | scrollOffset | number | 80 | Offset | | scrollBehavior | auto | smooth | smooth | Tipo de scroll | | disabled | boolean | false | Deshabilitado | | readOnly | boolean | false | Solo lectura |

6. Accesibilidad y SEO

| Prop | Tipo | Default | Descripción | | ------------------ | ------- | ---------------- | ------------------ | | ariaLabel | string | Menú desplegable | aria-label | | ariaLabelledBy | string | - | Etiqueta | | ariaDescribedBy | string | - | Descripción | | role | string | menu | Rol ARIA | | keyboardNavigation | boolean | true | Navegación teclado | | focusOnOpen | boolean | true | Foco inicial | | useAnchorTags | boolean | true | Usa <a> | | rel | string | - | Atributo rel |

7. Internacionalización

| Prop | Tipo | Default | Descripción | | ------------ | -------------------- | ------- | ----------- | | translations | DropdownTranslations | - | Textos | | direction | ltr | rtl | ltr | Dirección |

8. Renderizado personalizado

| Prop | Tipo | Descripción | | ------------- | -------------------------- | --------------------- | | renderTrigger | (props) => React.ReactNode | Trigger personalizado | | renderItem | (props) => React.ReactNode | Ítem personalizado | | renderSubmenu | (props) => React.ReactNode | Submenú personalizado |

9. Temas y variantes

| Prop | Tipo | Default | Descripción | | ----------- | ---------------------------------------------------------------- | ------- | ------------------ | | variant | default | minimal | elevated | borderless | dark | gradient | default | Variante visual | | customTheme | CustomTheme | - | Tema personalizado |



🎨 Ejemplos avanzados

Menú con apertura por click

<DropdownWithSubmenu
  menuItems={menuItems}
  openOnHover={false}
  closeOnClickOutside={true}
/>

Densidad compacta

<DropdownWithSubmenu
  menuItems={menuItems}
  density="compact"
  iconPosition="none"
/>

Tema oscuro

<DropdownWithSubmenu
  menuItems={menuItems}
  variant="dark"
  customTheme={{
    colors: { background: "#1e1e2f", text: "#ffffff" },
    borderRadius: 8,
    spacing: 1,
  }}
/>

Trigger personalizado

<DropdownWithSubmenu
  menuItems={menuItems}
  renderTrigger={({ isOpen, toggle, ref }) => (
    <button ref={ref} onClick={toggle} className="mi-boton">
      {isOpen ? "Cerrar" : "Abrir"} menú
    </button>
  )}
/>

Enlaces reales para SEO

<DropdownWithSubmenu
  menuItems={menuItems}
  useAnchorTags={true}
  rel="noopener noreferrer"
/>

♿ Accesibilidad

  • Rol menu / menuitem
  • Navegación por teclado
  • Atributos ARIA
  • Lectores de pantalla

📐 TypeScript

import type {
  MenuItem,
  DropdownVariant,
  DropdownDensity,
  IconPosition,
  ScrollBehavior,
  CustomTheme,
  DropdownTranslations,
  DropdownWithSubmenuProps,
} from "recursive-dropdown-mui";

🛠️ Desarrollo local

  1. Clona el repositorio\
  2. Instala dependencias: pnpm install\
  3. Construye: pnpm build\
  4. Dev: pnpm dev

📄 Licencia

MIT © BlackyCoder