Crea Aplicaciones Web Impresionantes con Material UI, Guía para Principiantes
Material UI es una biblioteca de componentes de interfaz de usuario para React que sigue los diseños de Material Design de Google. Proporciona componentes estilizados y listos para usar para acelerar el desarrollo de aplicaciones web y móviles. Material UI permite a los desarrolladores crear interfaces de usuario atractivas y funcionales con facilidad, mientras mantiene una consistencia en la apariencia y la experiencia de usuario.
Documentación oficial aquí: Material UI
En este tutorial estaremos trabajando en la versión 5 de MUI.
Material Design
Es un sistema de diseño de Google que se centra en proporcionar una experiencia de usuario coherente y atractiva en todos los dispositivos. Fue lanzado en 2014 y es un enfoque en la creación de una estética visual y de interacción sólida y estructurada.
Material Design utiliza elementos visuales sólidos y sombras para proporcionar una sensación de profundidad en la interfaz de usuario, y se basa en una escala de grises y en una paleta de colores limitada para proporcionar una apariencia uniforme y coherente.
La filosofía de Material Design se basa en la idea de que el software debe ser tangible, y que la interacción con él debe ser intuitiva y natural.
Ventajas de Material UI (MUI)
- Material UI sigue los diseños de Material Design de Google, lo que garantiza una experiencia de usuario coherente y atractiva.
- La biblioteca ofrece una amplia variedad de componentes estilizados y listos para usar, lo que ahorra tiempo y esfuerzo en el desarrollo.
- Material UI permite a los desarrolladores personalizar fácilmente los componentes para satisfacer sus necesidades específicas.
- Material UI es compatible con React, lo que significa que los desarrolladores pueden integrar fácilmente la biblioteca en sus proyectos existentes.
- Material UI ofrece una documentación completa y detallada para ayudar a los desarrolladores a aprovechar al máximo la biblioteca.
- Material UI cuenta con una comunidad activa y en constante crecimiento, lo que significa que los desarrolladores pueden obtener soporte y soluciones a problemas de manera eficiente.
En este curso de Material UI aprenderás:
Cómo instalar y configurar Material UI en tu proyecto de React.js y crear interfaces de usuario atractivas y funcionales. Este curso te enseñará los conceptos clave de Material UI, incluyendo CSSBaseline, Container, Typography, Grid, ThemeProvider, y muchos más.
Practica el uso de componentes como Navbar, Drawer, Button, Card, y explora componentes avanzados como AppBar, Alert, Snackbar, entre otros.
Además, para finalizar el curso, pondrás en práctica tus conocimientos creando una emocionante aplicación web de clima (Weather App) desde cero. Podrás buscar ciudades y obtener el clima actual al instante. No te pierdas la oportunidad de crear una experiencia única para tus usuarios.
¡Domina Material UI en proyectos de React.js para crear interfaces impresionantes y funcionales!
Github
Encuentra todo el código del curso aquí: código del curso MUI
Ayúdame a seguir creando contenido 😍
Tienes varias jugosas alternativas:
- Suscríbete al canal de Youtube (es gratis) click aquí
- Si estás viendo un video no olvides regalar un 👍 like y comentario 🙏🏼
- También puedes ser miembro del canal de Youtube click aquí
- Puedes adquirir cursos premium en Udemy 👇🏼👇🏼👇🏼
Muchas gracias por su tremendo apoyo 😊
Ramas
Instalación de MUI
Nuevo proyecto de react:
npm create vite@latest .
npm i
Instalación de Material UI:
npm install @mui/material @emotion/react @emotion/styled
npm install @fontsource/roboto
npm install @mui/icons-material
npm install @mui/lab #LoadingButton etc
Material UI utiliza Emotion como su motor de estilos predeterminado. Emotion es una biblioteca de estilos CSS-in-JS que se puede utilizar con React, React Native, Vue, Angular, Ember, Svelte, Preact, Inferno, etc.
Ejemplo rápido
import { Button } from "@mui/material";
<Button variant="contained">Hello World</Button>
CssBaseline
CssBaseline: Material UI proporciona un componente CssBaseline opcional. Corrige algunas incoherencias entre navegadores y dispositivos al tiempo que proporciona restablecimientos que se adaptan mejor a la interfaz de usuario de Material que las hojas de estilo globales alternativas como normalize.css.
CssBaseline
incluye una serie de reglas CSS que establecen valores para propiedades como la tipografía, los márgenes y los paddings, entre otros. Estas reglas son aplicadas a toda la aplicación y se aseguran de que todos los componentes tengan una apariencia consistente.
main.jsx
import { CssBaseline } from "@mui/material";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<CssBaseline />
<App />
</React.StrictMode>
);
Sin embargo, es posible que esté migrando progresivamente un sitio web a MUI, y usar un restablecimiento global podría no ser una opción. Es posible aplicar la línea de base solo a los niños usando el ScopedCssBaseline
componente.
import { ScopedCssBaseline } from "@mui/material";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<ScopedCssBaseline>
<App />
</ScopedCssBaseline>
</React.StrictMode>
);
Container
import { Container } from "@mui/material";
<Container>
<h1>Material UI</h1>
<Button variant="contained">mi primer componente</Button>
</Container>
maxWidth: Determina el ancho máximo del contenedor. El ancho del contenedor crece con el tamaño de la pantalla. Establézcalo en falso para deshabilitar maxWidth.
The sx prop: es un atajo para definir estilos personalizados que tienen acceso al tema.
<Container
maxWidth="xs"
sx={{
border: 2,
boxShadow: 1,
pb: 2,
}}
>
<h1>Material UI</h1>
<Button variant="contained">mi primer componente</Button>
</Container>
Typography
Recordar que instalamos: npm install @fontsource/roboto
y en nuestro main.jsx importamos:
import "@fontsource/roboto/300.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";
Ejemplo:
<Typography variant="h1">Soy un h1</Typography>
<Typography variant="h2">Soy un h2</Typography>
<Typography variant="h3">Soy un h3</Typography>
Changing the semantic element
La variante está asociada con un elemento semántico HTML predeterminado. Puede cambiar el elemento semántico predeterminado utilizando el component
prop.
<Typography
variant="h1"
component="h2"
>
Soy un h1
</Typography>
System props
Este componente es compatible con todos system props, por ejemplo podemos aplicar un margin top:
<Typography
variant="h1"
component="h2"
mb={2}
color="primary"
align="center"
boxShadow={2}
pb={2}
>
Soy un h1
</Typography>
Box
- El componente Box sirve como un componente contenedor para la mayoría de las necesidades de la utilidad CSS.
- box
- The sx prop: es un atajo para definir estilos personalizados que tienen acceso al tema.
- properties
- example
<Box
sx={{
border: 2,
borderColor: "peru",
p: 2,
bgcolor: "#111",
color: "white",
}}
>
<Typography align="center">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum saepe quas
laudantium, enim praesentium maiores beatae impedit vero dolorum
dignissimos, assumenda ipsam? Similique, mollitia commodi ducimus aliquid
voluptate molestias laborum!
</Typography>
</Box>
Theming
Vamos a cambiar la personalización de nuestro tema por defecto.
import CssBaseline from "@mui/material/CssBaseline";
import { createTheme, ThemeProvider } from "@mui/material/styles";
const theme = createTheme({
palette: {
primary: {
main: "#000e35",
},
secondary: {
main: "#f50057",
},
},
});
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<CssBaseline />
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</React.StrictMode>
);
Button & Icons
- Button & Icons
npm install @mui/icons-material
- listado de iconos
import Container from "@mui/material/Container";
import Button from "@mui/material/Button";
import AirplanemodeActiveIcon from "@mui/icons-material/AirplanemodeActive";
import AndroidIcon from "@mui/icons-material/Android";
export default function App() {
return (
<Container>
<Button
variant="contained"
color="info"
startIcon={<AirplanemodeActiveIcon />}
>
Botón personalizado
</Button>
<Button
variant="contained"
color="secondary"
endIcon={<AndroidIcon />}
>
Botón 2
</Button>
</Container>
);
}
Grid
Se basa en 12 columnas al igual que Bootstrap. De esta manera podemos crear un layout responsive a través de Flexbox.
import Grid from "@mui/material/Grid";
import Container from "@mui/material/Container";
export default function App() {
return (
<Container>
<Grid
container
spacing={2}
>
<Grid
item
xs={12}
sm={6}
md={4}
>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Qui
aspernatur perferendis ipsum veniam, nostrum eos quaerat sequi sed,
quisquam minima provident, ad magnam saepe impedit voluptatem
ratione quas molestias! Nisi.
</p>
</Grid>
<Grid
item
xs={12}
sm={6}
md={4}
>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Qui
aspernatur perferendis ipsum veniam, nostrum eos quaerat sequi sed,
quisquam minima provident, ad magnam saepe impedit voluptatem
ratione quas molestias! Nisi.
</p>
</Grid>
<Grid
item
xs={12}
sm={12}
md={4}
>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Qui
aspernatur perferendis ipsum veniam, nostrum eos quaerat sequi sed,
quisquam minima provident, ad magnam saepe impedit voluptatem
ratione quas molestias! Nisi.
</p>
</Grid>
</Grid>
</Container>
);
}
MiniPráctica Card Product
Paper es un componente en Material-UI que proporciona un contenedor elevado y sombreado. Se utiliza para encerrar contenido y darle un aspecto elevado en la página. Paper es un componente versátil que puede ser utilizado para crear tarjetas, dialogos, menús desplegables, entre otros.
Los estilos y la apariencia de Paper pueden ser personalizados utilizando los props de Material-UI o a través de CSS. También es posible anidar varios componentes Paper para crear diseños más complejos.
Ejemplo de uso:
styled()
styled()
es una función en Material-UI que permite crear componentes personalizados con estilos.
Es una forma de agregar estilos a componentes personalizados con una sintaxis similar a los componentes CSS.
La función styled()
se utiliza en conjunto con el paquete @emotion/styled
, que proporciona una forma sencilla de aplicar estilos CSS en JavaScript.
import { styled } from "@mui/material/styles";
import { Button, Paper } from "@mui/material";
import { Box } from "@mui/system";
const Img = styled("img")({
width: 200,
height: "100%",
objectFit: "cover",
objectPosition: "center",
});
export default function Product() {
return (
<Paper
sx={{
display: "flex",
alignItems: "center",
gap: 2,
overflow: "hidden",
mt: 5,
}}
>
<Img
src="https://via.placeholder.com/200"
alt="random"
/>
<Box sx={{ flexGrow: 1 }}>
<h2>Product Name</h2>
<p>Product Description</p>
<Button variant="contained">Add cart</Button>
</Box>
<Box
component="p"
sx={{ mr: 2 }}
>
$19.99
</Box>
</Paper>
);
}
Card
import {
Button,
Card,
CardActionArea,
CardActions,
CardContent,
CardMedia,
} from "@mui/material";
export default function MyCard() {
return (
<Card
sx={{
transition: "0.2s",
"&:hover": {
transform: "scale(1.05)",
},
}}
>
<CardActionArea>
<CardMedia
component="img"
image="https://via.placeholder.com/1000x200"
height="200"
alt="Card Image"
/>
<CardContent>
<h2>Card Title</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis
deserunt optio exercitationem, fugit enim saepe iusto magnam ipsam
est cumque hic deleniti sequi neque soluta quas! Accusamus voluptate
alias optio.
</p>
</CardContent>
</CardActionArea>
<CardActions>
<Button variant="contained">Add</Button>
<Button>Remove</Button>
</CardActions>
</Card>
);
}
List
aria-label
El atributo aria-label es un atributo ARIA (Accessible Rich Internet Applications) que proporciona una descripción textual para un elemento.
En este caso, el valor "main mailbox folders" proporciona una descripción de la sección de navegación que contiene enlaces a las carpetas principales de correo. Esto puede ser útil para los usuarios que utilizan lectores de pantalla o tecnologías de asistencia para mejorar la accesibilidad.
NavListDrawer.jsx
import {
Divider,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { Box } from "@mui/system";
import InboxIcon from "@mui/icons-material/Inbox";
import DraftsIcon from "@mui/icons-material/Drafts";
export default function NavListDrawer() {
return (
<Box sx={{ width: 250, bgcolor: "lightsalmon" }}>
<nav aria-label="main mailbox folders">
<List>
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Draft" />
</ListItemButton>
</ListItem>
</List>
</nav>
<Divider />
<nav aria-label="secondary trash spam">
<List>
<ListItem disablePadding>
<ListItemButton
component="a"
href="#trash"
>
<ListItemText primary="Trash" />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton
component="a"
href="#spam"
>
<ListItemText primary="Spam" />
</ListItemButton>
</ListItem>
</List>
</nav>
</Box>
);
}
import {
Divider,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { Box } from "@mui/system";
import InboxIcon from "@mui/icons-material/Inbox";
import DraftsIcon from "@mui/icons-material/Drafts";
const mainList = [
{
text: "Inbox",
icon: <InboxIcon />,
href: "#inbox",
},
{
text: "Drafts",
icon: <DraftsIcon />,
href: "#drafts",
},
];
const secondaryList = [
{
text: "Trash",
href: "#trash",
},
{
text: "Spam",
href: "#spam",
},
];
export default function NavListDrawer() {
return (
<Box sx={{ width: 250, bgcolor: "lightsalmon" }}>
<nav aria-label="main mailbox folders">
<List>
{mainList.map((item) => (
<ListItem
disablePadding
key={item.text}
>
<ListItemButton
href={item.href}
component="a"
>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.text} />
</ListItemButton>
</ListItem>
))}
</List>
</nav>
<Divider />
<nav aria-label="secondary trash spam">
<List>
{secondaryList.map((item) => (
<ListItem
disablePadding
key={item.text}
>
<ListItemButton
component="a"
href={item.href}
>
<ListItemText primary={item.text} />
</ListItemButton>
</ListItem>
))}
</List>
</nav>
</Box>
);
}
Drawer
role="presentation"
role="presentation"
es un atributo de HTML que se utiliza en elementos de HTML para especificar su rol en un documento o aplicación. Este atributo especifica que el elemento no tiene un rol semántico específico y que su uso se limita a la presentación visual.
En el caso de Material-UI, se utiliza role="presentation"
en un elemento para indicar que el elemento es solo para la presentación y no debe tener un comportamiento específico en el contexto de la accesibilidad, como ser un enlace o un elemento de menú. Esto ayuda a mejorar la accesibilidad de la aplicación y a evitar errores en el comportamiento de la accesibilidad.
App.jsx
import { Container, Button, Drawer } from "@mui/material";
import { useState } from "react";
import NavListDrawer from "./components/NavListDrawer";
export default function App() {
const [open, setOpen] = useState(false);
return (
<Container sx={{ display: "grid", gap: 4 }}>
<Button
variant="contained"
onClick={() => setOpen(true)}
>
Open Drawer
</Button>
<Drawer
anchor="left"
open={open}
onClose={() => setOpen(false)}
>
<NavListDrawer onClick={() => setOpen(false)} />
</Drawer>
</Container>
);
}
NavListDrawer.jsx
export default function NavListDrawer({ onClick }) {
return (
<Box
sx={{ width: 250 }}
onClick={onClick}
>
...
</Box>
);
}
AppBar
Ayúdame a seguir creando contenido 😍
Tienes varias jugosas alternativas:
- Suscríbete al canal de Youtube (es gratis) click aquí
- Si estás viendo un video no olvides regalar un 👍 like y comentario 🙏🏼
- También puedes ser miembro del canal de Youtube click aquí
- Puedes adquirir cursos premium en Udemy 👇🏼👇🏼👇🏼
Muchas gracias por su tremendo apoyo 😊
AppBar: El atributo position
en AppBar
se utiliza para especificar la posición del AppBar. El valor "static" indica que la barra de aplicaciones se mantendrá en su posición original en la página, sin flotar o estar fijada en la parte superior de la página. Esto significa que la barra de aplicaciones no se moverá mientras se desplaza por la página.
Los otros valores posibles para position son "fixed" y "sticky", que fijan la barra de aplicaciones en la parte superior de la página mientras se desplaza por ella.
Toolbar: proporciona un área de contenido para elementos como títulos, botones, menús, formularios y otros elementos. Además tiene configurado un display: flex
.
IconButton: El atributo edge
en IconButton
se utiliza para especificar la ubicación de un botón con icono en un AppBar. En este caso se ubicará en la esquina superior izquierda.
import { AppBar, Button, IconButton, Toolbar, Typography } from "@mui/material";
import MenuIcon from "@mui/icons-material/Menu";
export default function Navbar() {
return (
<AppBar position="static">
<Toolbar>
<IconButton
color="inherit"
size="large"
edge="start"
aria-label="menu"
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
sx={{ flexGrow: 1 }}
>
News
</Typography>
<Button color="inherit">Home</Button>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
);
}
Ejemplo con React Router
import { AppBar, Button, Container, Toolbar, Typography } from "@mui/material";
import AddToDriveIcon from "@mui/icons-material/AddToDrive";
import { NavLink } from "react-router-dom";
const Navbar = () => {
return (
<AppBar position="static">
<Container maxWidth="md">
<Toolbar>
<AddToDriveIcon />
<Typography
variant="h6"
sx={{ ml: 1, flexGrow: 1 }}
>
MyCompany
</Typography>
<Button
color="inherit"
component={NavLink}
to="/"
sx={{ pt: 1 }}
>
Home
</Button>
<Button
color="inherit"
component={NavLink}
to="/contact"
sx={{ pt: 1 }}
>
Contact
</Button>
</Toolbar>
</Container>
</AppBar>
);
};
export default Navbar;
isActive
<NavLink
to="/"
style={{ textDecoration: "none" }}
>
{({ isActive }) => (
<Button
color="inherit"
sx={{
pt: 1,
color: isActive ? "salmon" : "white",
}}
>
Home
</Button>
)}
</NavLink>
<Button
component={NavLink}
to="/"
sx={{ pt: 1 }}
style={({ isActive }) => (isActive ? { color: "black" } : { color: "white" })}
>
Home
</Button>
AppBar + Drawer
import {
AppBar,
Button,
Drawer,
IconButton,
Toolbar,
Typography,
} from "@mui/material";
import MenuIcon from "@mui/icons-material/Menu";
import NavListDrawer from "./NavListDrawer";
import { useState } from "react";
export default function Navbar() {
const [open, setOpen] = useState(false);
return (
<>
<AppBar position="static">
<Toolbar>
<IconButton
color="inherit"
size="large"
edge="start"
aria-label="menu"
onClick={() => setOpen(true)}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
sx={{ flexGrow: 1 }}
>
News
</Typography>
<Button color="inherit">Home</Button>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
<Drawer
anchor="left"
open={open}
onClose={() => setOpen(false)}
>
<NavListDrawer onClick={() => setOpen(false)} />
</Drawer>
</>
);
}
AppBar + Drawer + Responsive
NavbarDrawerResponsive.jsx
import {
AppBar,
Button,
Drawer,
IconButton,
Toolbar,
Typography,
} from "@mui/material";
import MenuIcon from "@mui/icons-material/Menu";
import { useState } from "react";
import NavListDrawerResponsive from "./NavListDrawerResponsive";
import { Box } from "@mui/system";
const navLinks = [
{ title: "Home", path: "#" },
{ title: "Login", path: "#login" },
{ title: "Register", path: "#register" },
];
export default function Navbar() {
const [open, setOpen] = useState(false);
return (
<>
<AppBar position="static">
<Toolbar>
<IconButton
color="inherit"
size="large"
edge="start"
aria-label="menu"
onClick={() => setOpen(true)}
sx={{ display: { xs: "block", sm: "none" } }}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
sx={{ flexGrow: 1 }}
>
News
</Typography>
<Box sx={{ display: { xs: "none", sm: "block" } }}>
{navLinks.map((link) => (
<Button
key={link.title}
sx={{ color: "#fff" }}
href={link.path}
>
{link.title}
</Button>
))}
</Box>
</Toolbar>
</AppBar>
<Drawer
anchor="left"
open={open}
onClose={() => setOpen(false)}
sx={{ display: { xs: "block", sm: "none" } }}
>
<NavListDrawerResponsive
onClick={() => setOpen(false)}
navLinks={navLinks}
/>
</Drawer>
</>
);
}
NavListDrawerResponsive.jsx
import {
Divider,
List,
ListItem,
ListItemButton,
ListItemText,
} from "@mui/material";
import { Box } from "@mui/system";
export default function NavListDrawerResponsive({ onClick, navLinks }) {
return (
<Box
sx={{ width: 250 }}
onClick={onClick}
>
<nav aria-label="main mailbox folders">
<List>
{navLinks.map((item) => (
<ListItem
disablePadding
key={item.title}
>
<ListItemButton
href={item.path}
component="a"
>
{/* <ListItemIcon>{item.icon}</ListItemIcon> */}
<ListItemText primary={item.title} />
</ListItemButton>
</ListItem>
))}
</List>
</nav>
<Divider />
</Box>
);
}
AppBar + React Router 6
npm install react-router-dom@6
BrowserRouter
BrowserRouter es un componente que se utiliza en React para permitir la navegación por una aplicación web utilizando la [API de Historial HTML5]. Cuando se utiliza BrowserRouter, se establece una correspondencia entre las rutas definidas en la aplicación y las URL que se utilizan en el navegador.
main.jsx
import { BrowserRouter } from "react-router-dom";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<BrowserRouter>
<CssBaseline />
<App />
</BrowserRouter>
</ThemeProvider>
</React.StrictMode>
);
Routes & Route
El componente Routes
es el componente principal que se utiliza para definir un conjunto de rutas que la aplicación puede manejar. Este componente se utiliza para definir las rutas principales y secundarias de la aplicación. Dentro de Routes
, se pueden definir varias instancias del componente Route
para especificar qué componentes deben renderizarse para cada ruta.
El componente Route
se utiliza para definir una ruta específica dentro de la aplicación. Para definir una ruta, se utiliza la prop path
para especificar la URL que se va a asociar con el componente que se va a renderizar. El componente Route
también acepta una prop element
que especifica el componente que se debe renderizar cuando se accede a la ruta especificada.
App.jsx
import { Route, Routes } from "react-router-dom";
<Routes>
<Route
path="/"
element={<Home />}
/>
<Route
path="/login"
element={<Login />}
/>
<Route
path="/register"
element={<Register />}
/>
</Routes>
NavLink
El componente NavLink
es un componente proporcionado por la librería "react-router-dom" que se utiliza para crear enlaces de navegación en una aplicación de React.
Navbar.jsx
import { NavLink } from "react-router-dom";
// navegationLinks.map
<Button
color="inherit"
key={item.title}
component={NavLink}
to={item.path}
>
{item.title}
</Button>
// Drawer
<NavListDrawer
navegationLinks={navegationLinks}
component={NavLink}
setOpen={setOpen}
/>
NavListDrawer.jsx
<ListItemButton
component={component}
to={item.path}
onClick={() => setOpen(false)}
>
Alert
<Alert severity="error">This is an error alert — check it out!</Alert>
<Alert severity="warning">This is a warning alert — check it out!</Alert>
<Alert severity="info">This is an info alert — check it out!</Alert>
<Alert severity="success">This is a success alert — check it out!</Alert>
Separación con grid
<Box sx={{ display: "grid", gap: "1rem" }}>
<Alert severity="error">Esta es una alerta</Alert>
<Alert severity="warning">This is a warning alert — check it out!</Alert>
<Alert severity="info">This is an info alert — check it out!</Alert>
<Alert severity="success">This is a success alert — check it out!</Alert>
</Box>
Con título
<Alert severity="error">
<AlertTitle>Error</AlertTitle>
Esta es una alerta
</Alert>
Actions
import { Alert, Button, Collapse } from "@mui/material";
import { Box } from "@mui/system";
import { useState } from "react";
export default function Home() {
const [open, setOpen] = useState(true);
return (
<>
<h1>Home</h1>
<Box sx={{ display: "grid", gap: "1rem" }}>
<Alert
severity="warning"
action={<Button color="inherit">Cerrar</Button>}
>
This is a warning alert — check it out!
</Alert>
<Collapse in={open}>
<Alert
severity="info"
onClose={() => {
setOpen(false);
}}
>
This is an info alert — check it out!
</Alert>
</Collapse>
<Alert
severity="warning"
action={<Button color="inherit">Cerrar</Button>}
>
This is a warning alert — check it out!
</Alert>
</Box>
</>
);
}
Dos botones
<Alert
severity="warning"
action={
<>
<Button color="primary">Update</Button>
<Button color="error">Delete</Button>
</>
}
>
This is a warning alert — check it out!
</Alert>
import { Alert } from "@mui/material";
import AutorenewIcon from "@mui/icons-material/Autorenew";
export default function Home() {
return (
<>
<h1>Home</h1>
<Alert
severity="warning"
icon={<AutorenewIcon fontSize="inherit" />}
>
This is a warning alert — check it out!
</Alert>
</>
);
}
Variante: outlined & filled
<Alert
severity="warning"
variant="outlined"
>
This is a warning alert — check it out!
</Alert>z
Snackbars
Ayúdame a seguir creando contenido 😍
Tienes varias jugosas alternativas:
- Suscríbete al canal de Youtube (es gratis) click aquí
- Si estás viendo un video no olvides regalar un 👍 like y comentario 🙏🏼
- También puedes ser miembro del canal de Youtube click aquí
- Puedes adquirir cursos premium en Udemy 👇🏼👇🏼👇🏼
Muchas gracias por su tremendo apoyo 😊
import { Alert, Button, Snackbar } from "@mui/material";
import { useState } from "react";
export default function Home() {
const [open, setOpen] = useState(false);
return (
<>
<h1>Home</h1>
<Button
onClick={() => setOpen(true)}
variant="contained"
>
Open
</Button>
<Snackbar
open={open}
autoHideDuration={1000}
onClose={() => setOpen(false)}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
>
<Alert
severity="warning"
variant="filled"
>
This is a warning alert — check it out!
</Alert>
</Snackbar>
</>
);
}
notistack
npm i notistack
import { SnackbarProvider } from "notistack";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<CssBaseline />
<ThemeProvider theme={theme}>
<SnackbarProvider
maxSnack={3}
autoHideDuration={2000}
>
<App />
</SnackbarProvider>
</ThemeProvider>
</React.StrictMode>
);
En el componente donde se quiere usar:
import { useSnackbar } from "notistack";
const { enqueueSnackbar } = useSnackbar();
const handleClick = () => {
enqueueSnackbar("Product added to cart", {
variant: "success",
anchorOrigin: {
vertical: "top",
horizontal: "right",
},
});
};
// anchorOrigin:
// Type: { horizontal: left | center | right, vertical: top | bottom }
// Default: { horizontal: left, vertical: bottom }
// variant:
// Type: default | error | success | warning | info
// Default: default
<Button
variant="contained"
onClick={handleClick}
>
Add cart
</Button>
mui/lab
npm install @mui/lab
import LoadingButton from "@mui/lab/LoadingButton";
const { enqueueSnackbar } = useSnackbar();
const [loading, setLoading] = useState(false);
const handleClick = () => {
setLoading(true);
setTimeout(() => {
setLoading(false);
enqueueSnackbar("Product added to cart", {
variant: "success",
anchorOrigin: {
vertical: "top",
horizontal: "right",
},
});
}, 2000);
};
<LoadingButton
variant="contained"
onClick={handleClick}
loading={loading}
>
Add cart
</LoadingButton>
Text Field
<TextField
id="outlined-basic"
label="Outlined"
variant="outlined"
/>
Formulario:
import { Box, Button, TextField } from "@mui/material";
import { useState } from "react";
export default function Register() {
const [email, setEmail] = useState("");
const [error, setError] = useState({
error: false,
message: "",
});
const emailValidation = (email) => {
// expresion regular para validar email
const regex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
return regex.test(email);
};
const onSubmit = (e) => {
e.preventDefault();
if (!emailValidation(email)) {
setError({
error: true,
message: "El email no es valido",
});
return;
}
console.log(email);
setError({
error: false,
message: "",
});
};
return (
<>
<h1>Register</h1>
<Box
component="form"
onSubmit={onSubmit}
autoComplete="off"
>
<TextField
label="Email"
variant="outlined"
id="email"
type="email"
fullWidth
required
error={error.error}
helperText={error.message}
onChange={(e) => setEmail(e.target.value)}
value={email}
/>
<Button
variant="outlined"
type="submit"
sx={{ mt: 2 }}
>
Submit
</Button>
</Box>
</>
);
}
Práctica Weather App
Links
Install
npm create vite@latest .
npm i
npm install @mui/material @emotion/react @emotion/styled @fontsource/roboto @mui/icons-material @mui/lab notistack
main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "@fontsource/roboto/300.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";
import "./index.css";
import { CssBaseline } from "@mui/material";
import { SnackbarProvider } from "notistack";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<CssBaseline />
<SnackbarProvider>
<App />
</SnackbarProvider>
</React.StrictMode>
);
.env.local
VITE_API_KEY=YOUR_API_KEY
console.log(import.meta.env.VITE_API_KEY);
API
https://api.weatherapi.com/v1/current.json?key=tuApiKey&lang=es&q=london
const API_WEATHER = `http://api.weatherapi.com/v1/current.json?key=${
import.meta.env.VITE_API_KEY
}&lang=es&q=`;
Code
App.jsx
import { LoadingButton } from "@mui/lab";
import { Box, Container, TextField, Typography } from "@mui/material";
import { useState } from "react";
const API_WEATHER = `http://api.weatherapi.com/v1/current.json?key=${
import.meta.env.VITE_API_KEY
}&lang=es&q=`;
export default function App() {
const [city, setCity] = useState("");
const [error, setError] = useState({
error: false,
message: "",
});
const [loading, setLoading] = useState(false);
const [weather, setWeather] = useState({
city: "",
country: "",
temperature: 0,
condition: "",
conditionText: "",
icon: "",
});
const onSubmit = async (e) => {
e.preventDefault();
setError({ error: false, message: "" });
setLoading(true);
try {
if (!city.trim()) throw { message: "El campo ciudad es obligatorio" };
const res = await fetch(API_WEATHER + city);
const data = await res.json();
if (data.error) {
throw { message: data.error.message };
}
console.log(data);
setWeather({
city: data.location.name,
country: data.location.country,
temperature: data.current.temp_c,
condition: data.current.condition.code,
conditionText: data.current.condition.text,
icon: data.current.condition.icon,
});
} catch (error) {
console.log(error);
setError({ error: true, message: error.message });
} finally {
setLoading(false);
}
};
return (
<Container
maxWidth="xs"
sx={{ mt: 2 }}
>
<Typography
variant="h3"
component="h1"
align="center"
gutterBottom
>
Weather App
</Typography>
<Box
sx={{ display: "grid", gap: 2 }}
component="form"
autoComplete="off"
onSubmit={onSubmit}
>
<TextField
id="city"
label="Ciudad"
variant="outlined"
size="small"
required
value={city}
onChange={(e) => setCity(e.target.value)}
error={error.error}
helperText={error.message}
/>
<LoadingButton
type="submit"
variant="contained"
loading={loading}
loadingIndicator="Buscando..."
>
Buscar
</LoadingButton>
</Box>
{weather.city && (
<Box
sx={{
mt: 2,
display: "grid",
gap: 2,
textAlign: "center",
}}
>
<Typography
variant="h4"
component="h2"
>
{weather.city}, {weather.country}
</Typography>
<Box
component="img"
alt={weather.conditionText}
src={weather.icon}
sx={{ margin: "0 auto" }}
/>
<Typography
variant="h5"
component="h3"
>
{weather.temperature} °C
</Typography>
<Typography
variant="h6"
component="h4"
>
{weather.conditionText}
</Typography>
</Box>
)}
<Typography
textAlign="center"
sx={{ mt: 2, fontSize: "10px" }}
>
Powered by:{" "}
<a
href="https://www.weatherapi.com/"
title="Weather API"
>
WeatherAPI.com
</a>
</Typography>
</Container>
);
}
⭐ Apoya el canal ⭐
Si quieres demostrar todo tu amor 😊, puedes comprar el curso en 👉🏽 Udemy: React + Firebase by bluuweb.