📘 ¿Quieres una base sólida de Ionic desde cero? Si prefieres aprender Ionic (y Firebase) de forma ordenada y de principio a fin, en lugar de ir saltando entre tutoriales sueltos, te dejo mi libro: Desarrollo de aplicaciones móviles con Ionic y Firebase desde cero.
Para crear un menú lateral en Ionic 8 colocas un componente <ion-menu> dentro de un <ion-split-pane>, lo conectas al ion-router-outlet mediante el atributo contentId y lo abres desde cualquier página con un <ion-menu-button>. Es el patrón clásico de navegación con menú deslizante (el típico botón de «hamburguesa» ☰) y en esta guía lo montamos paso a paso con el enfoque moderno de componentes standalone, que es como se trabaja hoy en Ionic.
Actualizado: julio 2026 — revisado con Ionic 8.8 y Angular standalone.
En el capítulo de navegación vimos cómo movernos entre páginas con rutas. El menú lateral es el siguiente paso natural: un cajón lateral desde el que el usuario salta a las secciones principales de la app. Aquí tienes el índice completo del tutorial:
Índice del tutorial de Ionic >>
- Tutorial de Ionic: Introducción
- Estructura de un proyecto en Ionic
- Mini juego de acertar números
- Navegación entre páginas
- Menú lateral (estás aquí)
- Navegación por tabs (pestañas)
- Componentes personalizados
- Peticiones HTTP a una API REST
- Formularios reactivos
Qué es el menú lateral en Ionic (ion-menu y split-pane)
Ionic resuelve el menú lateral con dos componentes que trabajan juntos:
ion-menu: el propio cajón deslizante. Se abre desde un lado de la pantalla (por defecto, el izquierdo) y contiene la lista de secciones.ion-split-pane: un contenedor que, en pantallas anchas (tablet, escritorio), deja el menú fijo a la izquierda en lugar de deslizante. En el móvil se comporta como un menú normal; en escritorio, como una barra lateral permanente. Con un solo componente cubres los dos casos.
La pieza clave es el atributo contentId: le dice al menú a qué contenido tiene que empujar cuando se abre. Ese contenido suele ser el ion-router-outlet, donde se renderizan tus páginas.
Crear el proyecto de partida
Arrancamos con la plantilla blank. Desde Ionic 7 el CLI genera proyectos standalone por defecto (sin NgModule), así que no hay que configurar nada extra:
ionic start menuApp blank --type=angular cd menuApp
Genera un par de páginas para tener a dónde navegar desde el menú:
ionic generate page inicio ionic generate page listado
Estructura básica: el menú en el componente raíz
El menú vive en el componente raíz de la app (app.component), porque tiene que estar disponible en todas las páginas. Empezamos por el bootstrap de la aplicación en main.ts, que es donde se registran Ionic y el router:
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { provideIonicAngular } from '@ionic/angular/standalone';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';
bootstrapApplication(AppComponent, {
providers: [
provideIonicAngular(),
provideRouter(routes),
],
});
Ahora el componente raíz. Fíjate en dos cosas propias del enfoque standalone: cada componente de Ionic se importa uno a uno desde @ionic/angular/standalone, y los iconos se registran a mano con addIcons (ya no se cargan todos de golpe, así el bundle es más ligero):
// app.component.ts
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
import { addIcons } from 'ionicons';
import { homeOutline, listOutline, settingsOutline } from 'ionicons/icons';
import {
IonApp, IonSplitPane, IonMenu, IonHeader, IonToolbar, IonTitle,
IonContent, IonList, IonListHeader, IonMenuToggle, IonItem,
IonIcon, IonLabel, IonRouterOutlet,
} from '@ionic/angular/standalone';
@Component({
selector: 'app-root',
standalone: true,
templateUrl: './app.component.html',
imports: [
RouterLink, RouterLinkActive,
IonApp, IonSplitPane, IonMenu, IonHeader, IonToolbar, IonTitle,
IonContent, IonList, IonListHeader, IonMenuToggle, IonItem,
IonIcon, IonLabel, IonRouterOutlet,
],
})
export class AppComponent {
// Las secciones que aparecerán en el menú
paginas = [
{ titulo: 'Inicio', url: '/inicio', icono: 'home-outline' },
{ titulo: 'Listado', url: '/listado', icono: 'list-outline' },
];
constructor() {
// Registra los iconos que vas a usar (nombre que usarás en la plantilla → icono importado)
addIcons({
'home-outline': homeOutline,
'list-outline': listOutline,
'settings-outline': settingsOutline,
});
}
}
Y la plantilla. Aquí es donde se ve el patrón completo: split-pane > menu + router-outlet, unidos por el mismo contentId:
<!-- app.component.html -->
<ion-app>
<ion-split-pane contentId="main-content">
<ion-menu contentId="main-content">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Menú</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-list-header>Secciones</ion-list-header>
@for (p of paginas; track p.url) {
<ion-menu-toggle auto-hide="false">
<ion-item
[routerLink]="p.url"
routerDirection="root"
routerLinkActive="selected"
lines="none"
detail="false">
<ion-icon slot="start" [name]="p.icono"></ion-icon>
<ion-label>{{ p.titulo }}</ion-label>
</ion-item>
</ion-menu-toggle>
}
</ion-list>
</ion-content>
</ion-menu>
<!-- El id DEBE coincidir con el contentId del menú y del split-pane -->
<ion-router-outlet id="main-content"></ion-router-outlet>
</ion-split-pane>
</ion-app>
Tres detalles que evitan el 90% de los dolores de cabeza:
- El
id="main-content"delion-router-outlettiene que coincidir exactamente con elcontentIddel menú y del split-pane. Si no coinciden, el menú no se abre y no verás ningún error. - El
@fores la sintaxis de control de flujo moderna de Angular (17+). Sustituye al viejo*ngFory no necesita importarCommonModule. ion-menu-toggleenvuelve cada opción para que el menú se cierre solo al pulsar. Elauto-hide="false"mantiene visibles los enlaces aunque el menú esté fijo en escritorio.
Añadir el botón para abrir el menú
El menú ya existe, pero en el móvil necesitas un botón para abrirlo. Se coloca en la barra de herramientas de cada página con ion-menu-button. Este es el HTML de la página de inicio:
<!-- inicio.page.html -->
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>Inicio</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<p>Soy la página de inicio. Pulsa ☰ para abrir el menú lateral.</p>
</ion-content>
Como cada página es standalone, recuerda importar en su .ts los componentes que usa la plantilla:
// inicio.page.ts
import { Component } from '@angular/core';
import {
IonHeader, IonToolbar, IonButtons, IonMenuButton,
IonTitle, IonContent,
} from '@ionic/angular/standalone';
@Component({
selector: 'app-inicio',
standalone: true,
templateUrl: './inicio.page.html',
imports: [
IonHeader, IonToolbar, IonButtons, IonMenuButton,
IonTitle, IonContent,
],
})
export class InicioPage {}
Con esto ya tienes un menú lateral funcionando: en el móvil se desliza al pulsar el botón; en pantalla ancha, gracias al split-pane, se queda fijo a la izquierda.
Menú fijo en escritorio: el split-pane responsive
Por defecto, el ion-split-pane muestra el menú fijo cuando la pantalla supera el breakpoint md (unos 768 px). Puedes cambiar a partir de qué tamaño se «ancla» el menú con el atributo when:
<!-- Menú fijo solo en pantallas grandes (≥ 992 px) --> <ion-split-pane contentId="main-content" when="lg"> ... </ion-split-pane> <!-- O con una media query personalizada --> <ion-split-pane contentId="main-content" when="(min-width: 1024px)"> ... </ion-split-pane>
Valores admitidos: xs, sm, md, lg, xl, una media query, o directamente false si nunca quieres el menú fijo (siempre deslizante).
Abrir, cerrar y alternar el menú por código
A veces necesitas controlar el menú desde el TypeScript (por ejemplo, cerrarlo tras una acción). Para eso está MenuController. En un proyecto standalone lo importas desde @ionic/angular/standalone y lo inyectas con inject():
// cualquier.page.ts
import { Component, inject } from '@angular/core';
import { MenuController } from '@ionic/angular/standalone';
@Component({ /* ... */ })
export class ListadoPage {
private menu = inject(MenuController);
abrirMenu() { this.menu.open(); }
cerrarMenu() { this.menu.close(); }
alternarMenu(){ this.menu.toggle(); }
}
Si tienes varios menús (uno a cada lado, por ejemplo), dales un menuId y pásaselo a los métodos:
<ion-menu contentId="main-content" menuId="ajustes" side="end"> ... </ion-menu>
this.menu.open('ajustes'); // abre el menú de la derecha
this.menu.enable(false, 'ajustes'); // lo deshabilita (útil según el rol del usuario)
Menús condicionales: ocultar opciones según el usuario
Un caso muy habitual: mostrar unas secciones u otras según si el usuario ha iniciado sesión o su rol. Como el menú se construye desde un array, basta con filtrarlo:
paginas = [
{ titulo: 'Inicio', url: '/inicio', icono: 'home-outline', soloAdmin: false },
{ titulo: 'Panel', url: '/panel', icono: 'settings-outline', soloAdmin: true },
];
get paginasVisibles() {
return this.paginas.filter(p => !p.soloAdmin || this.esAdmin);
}
Y en la plantilla recorres paginasVisibles en lugar de paginas. Sin trucos raros: el menú es UI normal de Angular.
Errores típicos (y cómo salir de ellos)
- El menú no se abre y no hay error. Casi siempre es que
contentIdy eliddelion-router-outletno coinciden. Revísalos carácter a carácter. - Los iconos salen en blanco. En standalone hay que registrarlos con
addIcons; si te olvidas de uno, aparece hueco. Comprueba que el nombre que pones enname="..."es exactamente la clave que registraste. - El botón ☰ no aparece en escritorio. Es lo esperado: cuando el split-pane ancla el menú, Ionic oculta el
ion-menu-buttonporque ya no hace falta. En móvil vuelve a mostrarse. - Import mal resuelto. Si mezclas imports de
@ionic/angulary de@ionic/angular/standaloneen el mismo proyecto standalone, tendrás comportamientos raros. En un proyecto standalone, importa todo (componentes y controladores) desde@ionic/angular/standalone.

Seguimos con el tutorial
Con el menú lateral ya montado, el siguiente paso lógico es dominar el otro gran patrón de navegación de Ionic: las pestañas. Lo tienes en el tutorial de Ionic tabs. Y cuando quieras conectar tu app a datos reales, sigue por peticiones HTTP a una API REST y por formularios reactivos.
📘 ¿Quieres aprender Ionic de forma ordenada, no a saltos? Todo esto (menú, tabs, HTTP, Firebase) y mucho más, paso a paso y con imágenes, en mi libro Desarrollo de aplicaciones móviles con Ionic y Firebase desde cero.
📱 ¿Prefieres que te desarrolle yo la app? Mira mi presupuesto para desarrollo de apps y cuéntame tu idea.
También puedes seguirme en Twitter en @revi_apps y no olvides que me ayudas mucho si compartes este post en las redes sociales.
