Publicado: Hace alrededor de 20 horas
Comenzaremos aprendiendo qué es un NavBar, dónde utilizarlo, cuáles son sus características principales y los errores más comunes al implementarlo. Esta base nos servirá para crear, paso a paso, un componente NavBar en React que cumpla con su objetivo principal: facilitar la navegación dentro de tu aplicación.
Un NavBar o Barra de Navegación es un elemento muy utilizado en aplicaciones frontend: web, escritorio o móviles. Su objetivo principal es permitir navegar entre las secciones más importantes de una aplicación.
Si necesitas darle visibilidad y acceso rápido a secciones principales de tu aplicación. Algunos ejemplos comunes son:
Antes de escribir cualquier línea de código debemos entender las características principales que componente un NavBar.
NOTA: Establecer las responsabilidades de un componente desde un inicio, nos permitirá diseñar componentes más escalables y fáciles de utilizar.
Los NavBars están compuestos de sub-componentes, donde cada uno, es responsable por una tarea específica. Los principales sub-componentes que un NavBar puede tener son:
Cada componente dentro del NavBar tiene una tarea específica:
Cuando llegamos a la implementación con los requerimientos claros y un diseño en mente, la implementación se vuelve más sencilla. Dicho esto, primero configuremos nuestro proyecto y una vez listo, comencemos a implementar nuestra solución.
En este tutorial trabajaremos con Create React App, una herramienta sencilla y muy práctica para proyectos de aprendizaje o ejemplos básicos. Su mayor ventaja es que viene lista para usar, con todo lo necesario para ejecutar pruebas en nuestros componentes.
NOTA: si tu objetivo es desarrollar aplicaciones en un entorno más profesional, te sugiero revisar opciones más modernas y rápidas como Vite, Parcel.js o Rsbuild, que ofrecen una mejor experiencia para crear aplicaciones actuales.
npx create-react-app navbar-demo cd modal-demo npm start
Una vez ejecutados los comandos, abre un pestaña en tu navegador y escribe:
Una vez que tenemos nuestro ambiente listo, comencemos definiendo la estructura básica de nuestros componentes.
src/components/NavBar/NavBar.js 1: import React from 'react'; 2: 3: const NavBar = () => { 4: return null 5: } 6: 7: export const Brand = () => { 8: return null 9: } 10: 11: export const Actions = () => { 12: return null 13: } 14: 15: export const Action = () => { 16: return null 17: } 18: 19: export const Nav = () => { 20: return null 21: } 22: 23: export const NavItem = () => { 24: return null 25: } 26: 27: export const Toggler = () => { 28: return null 29: } 30: 31: export default NavBar;
Ahora, lo siguiente que haremos es implementar la funcionalidad de cada uno de los subcomponentes para finalmente integrarlo al componente principal.
Componente Brand
El componente Brand es bastante sencillo porque su responsabilidad es mostrar un nombre o logotipo. Cómo solución, podemos hacer lo siguiente:
Cada una de las soluciones tiene sus ventajas y desventajas. Para nuestro caso, vamos a utilizar la segunda: Recibir un prop y texto como children, ya que se ajusta mejor a la responsabilidad del componente.
Veamos como luce la implementación:
src/components/NavBar/NavBar.js . . . 7: export const Brand = ({ children, logo }) => { 8: if (!children && !logo) { 9: throw new Error("Brand requiere al menos un texto o un logo.") 10: } 11: 12: if (children && typeof children !== "string") { 13: throw new Error("Brand solo acepta texto como children.") 14: } 15: 16: return ( 17: <> 18: {logo && <img src={logo} alt="logo" />} 19: {children && <span>{children}</span>} 20: </> 21: ) 22: } . . .
Para utilizarlo basta con hacer lo siguiente:
// Únicamente nombre <Brand>Un salto de línea</Brand> // Nombre o logo <Brand logo=‘/ligadelaimagen.png’ >Un salto de línea</Brand> // Únicamente logo <Brand logo=‘/ligadelaimagen.png’ />
Bastante sencillo, ¿cierto? Continuemos con el siguiente componente.
Componente Nav
Este componente tiene una particularidad: se comporta como un contenedor de elementos Nav. Cada NavItem es responsable de representar un enlace.
La solución para este caso es que Nav solo permita renderizar hijos de tipo NavItem. A su vez, cada NavItem debe recibir un prop to y un children de tipo texto.
Veamos la implementación:
src/components/NavBar/NavBar.js . . . 32: export const Nav = ({ children }) => { 33: const items = Children.toArray(children) 34: 35: items.forEach((child) => { 36: if (!React.isValidElement(child) || child.type !== NavItem) { 37: throw new Error("Nav solo puede renderizar hijos de tipo <NavItem />.") 38: } 39: }) 40: 41: return( 42: <div className='navbar-nav'> 43: {items} 44: </div> 45: ) 46: } 47: 48: export const NavItem = ({ to, children }) => { 49: if (typeof to !== "string" || to.trim() === "") { 50: throw new Error("NavItem requiere un prop 'to' no vacío.") 51: } 52: if (typeof children !== "string") { 53: throw new Error("NavItem solo acepta texto como children.") 54: } 55: 56: return <a href={to} className='nav-item'>{children}</a> 57: } . . .
Utilizarlo es bastante sencillo:
<Nav> <NavItem to=“/enlace1”>Enlace 1</NavItem> <NavItem to=“/enlace2”>Enlace 2</NavItem> <NavItem to=“/enlace3”>Enlace 3</NavItem> </Nav>
Simple, ¿cierto? Continuemos con el siguiente componente.
Componente Actions
Este componente es bastante similar al anterior. Sin embargo, el componente hijo Action es más flexible: permite cualquier renderizar cualquier hijo.
La solución es la siguiente:
src/components/NavBar/NavBar.js . . . 24: export const Actions = ({ children }) => { 25: const items = Children.toArray(children) 26: 27: items.forEach((child) => { 28: if (!React.isValidElement(child) || child.type !== Action) { 29: throw new Error("Actions solo puede renderizar hijos de tipo <Action />.") 30: } 31: }) 32: 33: return( 34: <div className='navbar-actions'> 35: {items} 36: </div> 37: ) 38: } 39: 40: export const Action = ({ children }) => { 41: return( 42: <div className='action'> 43: {children} 44: </div> 45: ) 46: } . . .
Para utilizarlo:
<Actions> <Action> <button>Haz clic</button> </Action> <Action> <form> </form> </Action> </Actions>
Listo, continuemos con el siguiente componente.
Componente TogglerEste componente sirve como un switch para ocultar o mostrar el componente Nav. ¿Quien debería de encargarse de esa comunicación? Así es, el componente principal NavBar. Entonces, ¿cómo debería verse este componente? Veamos la figura 3.
Agreguemos la lógica:
src/components/NavBar/NavBar.js . . . 75: export const Toggler = ({icon = '☰' }) => { 76: const [open, setOpen] = useState(false) 77: 78: const handleClick = () => { 79: const next = !open 80: setOpen(next) 81: } 82: 83: return ( 84: <button type="button" className="navbar-toggler" onClick={handleClick}> 85: {icon} 86: </button> 87: ) 88: } . . .
Listo.
Por último, integremos todos los componentes en el NavBar.
Componente NavBar
Lo primero es validar que el componente NavBar reciba únicamente elementos de tipo Brand, Actions, Nav y Toggler.
Veamos la solución:
src/components/NavBar/NavBar.js . . . 3: export const NavBar = ({ children }) => { 4: let brand = null 5: let actions = null 6: let nav = null 7: let toggler = null 8: 9: const items = Children.toArray(children) 10: 11: items.forEach((child) => { 12: if (!React.isValidElement(child)) { 13: throw new Error("NavBar solo puede renderizar componentes válidos.") 14: } 15: 16: switch (child.type) { 17: case Brand: 18: brand = child 19: break 20: case Actions: 21: actions = child 22: break 23: case Nav: 24: nav = child 25: break 26: case Toggler: 27: toggler = child 28: break 29: default: 30: throw new Error( 31: "NavBar solo puede renderizar hijos de tipo <Brand />, <Actions />, <Nav /> o <Toggler />." 32: ) 33: } 34: }) 35: 36: return ( 37: <nav className="navbar"> 38: {brand} 39: {nav} 40: {actions} 41: {toggler} 42: </nav> 43: ) 44: } . . .
De esta forma, garantizamos la ubicación de los elementos y evitamos que quien consuma el componente tenga que preocuparse por ello.
Por último, agreguemos la lógica para que el toggler oculte o muestre el componente nav:
src/components/NavBar/NavBar.js . . . 3: export const NavBar = ({ children }) => { . . . 36: // Si no hay Toggler, por defecto el Nav está visible 37: const initialOpen = toggler ? !!toggler.props?.initialOpen : true 38: const [isOpen, setIsOpen] = useState(initialOpen) 39: 40: // Encadena el onChange del Toggler hijo con el control del NavBar 41: const handleToggle = (next) => { 42: setIsOpen(next) 43: if (toggler?.props?.onChange) toggler.props.onChange(next) 44: } 45: 46: // Inyecta el onChange al Toggler (y respeta su initialOpen) 47: const togglerWithHandlers = 48: toggler && 49: cloneElement(toggler, { 50: onChange: handleToggle, 51: initialOpen: initialOpen, 52: }) 53: 54: return ( 55: <nav className="navbar"> 56: {brand} 57: {isOpen && nav} 58: {actions} 59: {togglerWithHandlers} 60: </nav> 61: ) 62: } . . . 132: export const Toggler = ({initialOpen, onChange, icon = '☰' }) => { 133: const [open, setOpen] = useState(initialOpen) 134: 135: const handleClick = () => { 136: const next = !open 137: setOpen(next) 138: onChange(next) 139: } 140: 141: return ( 142: <button type="button" className="navbar-toggler" onClick={handleClick}> 143: {icon} 144: </button> 145: ) 146: } . . .
De manera general, lo que hacemos es comprobar si existe un Toggler. En caso de ser así, lo clonamos para inicializar sus props onChange e initialOpen.
Listo, tenemos un componente NavBar que cumple con las características principales.
Ahora, podemos enfocarnos en darle estilos y agregar nuevas funcionalidades.
Eso sí, no olvides agregar pruebas. Esto permitirá mantener el componente confiable y facilitar su mantenimiento.
Si quieres aprender más sobre el tema, te recomiendo mi libro: Testing en React: Guía práctica con Jest y React Testing Library, donde aprenderás todo lo necesario para probar tus componentes como un profesional.
Para visualizar el resultado, te invito a ver la siguiente galería de imágenes 👇
Seguro has escuchado la frase “No revientar la rueda”. Básicamente, se refiere a evitar construir algo desde cero cuando ya existen soluciones probadas. En el caso de un NavBar, ya existen librerías y frameworks para usarlos, con estilos modernos y en la gran mayoría, bastantes flexibles.
A continuación, veamos algunas de las alternativas más recomendadas, junto con ejemplos de como se implementan.
NOTA: Cuando necesites inspiración para desarrollar tus componentes, revisa las librerías existentes. Es muy probable que encuentres buenas soluciones.
Una de las alternativas más robustas en el mercado desarrollada en React. Cuenta con una gran variedad de componentes, modernos y fáciles de utilizar. Pero no solo esto, es una solución pensada en proyectos grandes.
Entre sus componentes podemos encontrar el AppBar, que es un tipo de NavBar pero orientado a ser la barra principal de la aplicación en comparación con el NavBar donde puede cambiar según la pantalla donde estemos.
Su implementación es bastante sencillo, veamos un ejemplo:
import * as React from 'react'; import AppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; import Typography from '@mui/material/Typography'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import MenuIcon from '@mui/icons-material/Menu'; export default function ButtonAppBar() { return ( <Box sx={{ flexGrow: 1 }}> <AppBar position="static"> <Toolbar> <IconButton size="large" edge="start" color="inherit" aria-label="menu" sx={{ mr: 2 }} > <MenuIcon /> </IconButton> <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}> News </Typography> <Button color="inherit">Login</Button> </Toolbar> </AppBar> </Box> ); }
Para más detalle, consulta la documentación de Material UI sobre NavBar (AppBar)
Bootstrap
Una de las herramientas más antiguas y populares del mercado, reconocida por su gran cantidad de componentes y excelente documentación. Estas características la han convertido en una de las alternativas más utilizadas. Si bien, no esta diseñada para React, su documentación y la gran cantidad de ejemplos hacen que su integración sea bastante sencillo.
Caso contrario a Material UI, el componente NavBar lo vas a encontrar como NavBar.
Utilizarlo es bastante sencillo, veamos como podríamos utilizarlo:
<nav class="navbar navbar-expand-lg bg-body-tertiary"> <div class="container-fluid"> <a class="navbar-brand" href="#">Navbar</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link active" aria-current="page" href="#">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Link</a> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> Dropdown </a> <ul class="dropdown-menu"> <li><a class="dropdown-item" href="#">Action</a></li> <li><a class="dropdown-item" href="#">Another action</a></li> <li><hr class="dropdown-divider"></li> <li><a class="dropdown-item" href="#">Something else here</a></li> </ul> </li> <li class="nav-item"> <a class="nav-link disabled" aria-disabled="true">Disabled</a> </li> </ul> <form class="d-flex" role="search"> <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"/> <button class="btn btn-outline-success" type="submit">Search</button> </form> </div> </div> </nav>
Para saber más sobre el componente NavBar de Bootstrap, haz clic en aquí
React Bootstrap
Como su nombre lo indica, es una implementación de Bootstrap adaptada a React. La gran ventaja es que no necesitas trabajar con clases CSS, sino que tienes todos los componentes de Bootstrap implementados como componentes React, listos para usarse.
Veamos el siguiente ejemplo:
import Container from 'react-bootstrap/Container'; import Nav from 'react-bootstrap/Nav'; import Navbar from 'react-bootstrap/Navbar'; import NavDropdown from 'react-bootstrap/NavDropdown'; function BasicExample() { return ( <Navbar expand="lg" className="bg-body-tertiary"> <Container> <Navbar.Brand href="#home">React-Bootstrap</Navbar.Brand> <Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Collapse id="basic-navbar-nav"> <Nav className="me-auto"> <Nav.Link href="#home">Home</Nav.Link> <Nav.Link href="#link">Link</Nav.Link> <NavDropdown title="Dropdown" id="basic-nav-dropdown"> <NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item> <NavDropdown.Item href="#action/3.2"> Another action </NavDropdown.Item> <NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item> <NavDropdown.Divider /> <NavDropdown.Item href="#action/3.4"> Separated link </NavDropdown.Item> </NavDropdown> </Nav> </Navbar.Collapse> </Container> </Navbar> ); } export default BasicExample;
Para conocer más sobre el NavBar utilizando la librería React Bootstrap, haz clic en el siguiente enlace: https://react-bootstrap.netlify.app/docs/components/navbar/
Construir un NavBar en React desde cero no solo te ayuda a entender mejor cómo funciona la composición de componentes, también te permite crear soluciones a la medida de tu aplicación. Al separar responsabilidades en subcomponentes como Brand, Nav, Actions y Toggler, tu código se vuelve más claro, escalable y fácil de mantener.
Con esta base sólida, podrás agregar estilos, personalizar la experiencia en dispositivos móviles y extender la funcionalidad sin complicaciones.