Saltar al contenido

Clase 34 - Material UI

#react #materialui #mui

Material UI (MUI)

🤔 - ¿Qué es Material UI?

Material UI es una biblioteca de componentes React que implementa el sistema de diseño Material Design de Google. Proporciona componentes pre-construidos, personalizables y accesibles que siguen las mejores prácticas de diseño y usabilidad.

A diferencia de frameworks utility-first como TailwindCSS, Material UI adopta un enfoque “component-first”, ofreciendo componentes completos listos para usar con estilos y funcionalidades integradas.

💫 - Ventajas Principales

  • Desarrollo Rápido: Componentes completos y funcionales desde el primer momento.
  • Consistencia: Diseño cohesivo basado en Material Design con principios establecidos.
  • Accesibilidad: Componentes construidos siguiendo estándares WAI-ARIA.
  • Theming: Sistema robusto de temas para personalización global.
  • TypeScript: Soporte completo y tipado fuerte out-of-the-box.
  • Ecosistema: Amplia documentación, ejemplos y comunidad activa.

🧱 - Conceptos Básicos de Uso

🌱 - Componentes Core

Material UI funciona con componentes React importables que encapsulan estilos y lógica:

import { Button, Typography, Card, CardContent } from '@mui/material';
function MyComponent() {
return (
<Card>
<CardContent>
<Typography variant="h4" component="h1" gutterBottom>
Título Principal
</Typography>
<Typography variant="body1" color="text.secondary">
Contenido del componente con estilos automáticos
</Typography>
<Button variant="contained" color="primary">
Acción Principal
</Button>
</CardContent>
</Card>
);
}

🎭 - Sistema de Theming

MUI utiliza un sistema de temas centralizado basado en ThemeProvider:

import { createTheme, ThemeProvider } from '@mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
typography: {
h4: {
fontWeight: 600,
},
},
});
function App() {
return (
<ThemeProvider theme={theme}>
{/* Todos los componentes heredan el tema */}
<MyComponent />
</ThemeProvider>
);
}

📏 - Sistema de Espaciado

MUI usa un sistema de espaciado basado en una función de spacing (por defecto 8px):

  • sx={{ p: 2 }} = padding: 16px
  • sx={{ m: 1 }} = margin: 8px
  • sx={{ mt: 3, mb: 2 }} = margin-top: 24px, margin-bottom: 16px

📱 - Responsive Design

El sistema responsive se maneja a través de breakpoints en el tema:

import { useTheme, useMediaQuery } from '@mui/material';
function ResponsiveComponent() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
return (
<Box sx={{
display: 'flex',
flexDirection: { xs: 'column', md: 'row' },
gap: { xs: 1, md: 2 },
p: { xs: 2, md: 4 }
}}>
{/* Contenido responsive */}
</Box>
);
}

Breakpoints predefinidos:

  • xs: - 0px y superior
  • sm: - 600px y superior
  • md: - 900px y superior
  • lg: - 1200px y superior
  • xl: - 1536px y superior

⚙️ - Sistema sx

La prop sx permite escribir estilos CSS-in-JS de manera eficiente:

<Box
sx={{
backgroundColor: 'primary.main',
color: 'primary.contrastText',
p: 2,
borderRadius: 2,
boxShadow: 3,
'&:hover': {
backgroundColor: 'primary.dark',
},
}}
>
Elemento con estilos personalizados
</Box>

🌙 - Dark Mode

MUI incluye soporte nativo para modo oscuro:

import { createTheme, ThemeProvider, CssBaseline } from '@mui/material/styles';
const theme = createTheme({
palette: {
mode: 'dark', // 'light' o 'dark'
},
});
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline /> {/* Resetea estilos base */}
<MyComponent />
</ThemeProvider>
);
}

⭐ - Ejemplos Prácticos

🎴 - Card Component Completo

import {
Card,
CardContent,
CardMedia,
CardActions,
Typography,
Button,
Chip,
Box
} from '@mui/material';
function ProductCard({ product }) {
return (
<Card
sx={{
maxWidth: 345,
boxShadow: 3,
'&:hover': { boxShadow: 6 }
}}
>
<CardMedia
component="img"
height="200"
image={product.image}
alt={product.title}
/>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<Chip
label={product.category}
color="primary"
size="small"
/>
</Box>
<Typography variant="h6" component="h2" gutterBottom>
{product.title}
</Typography>
<Typography variant="body2" color="text.secondary">
{product.description}
</Typography>
<Typography variant="h6" color="primary" sx={{ mt: 2 }}>
${product.price}
</Typography>
</CardContent>
<CardActions>
<Button size="small" variant="contained">
Comprar
</Button>
<Button size="small" variant="outlined">
Ver detalles
</Button>
</CardActions>
</Card>
);
}

📝 - Formulario con Validación

import {
Box,
TextField,
Button,
Typography,
Paper,
Alert
} from '@mui/material';
import { useState } from 'react';
function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
// Lógica de validación y envío
};
return (
<Paper elevation={3} sx={{ p: 4, maxWidth: 500, mx: 'auto' }}>
<Typography variant="h4" component="h1" gutterBottom>
Contáctanos
</Typography>
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 2 }}>
<TextField
fullWidth
label="Nombre"
name="name"
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
error={!!errors.name}
helperText={errors.name}
margin="normal"
required
/>
<TextField
fullWidth
label="Email"
name="email"
type="email"
value={formData.email}
onChange={(e) => setFormData({...formData, email: e.target.value})}
error={!!errors.email}
helperText={errors.email}
margin="normal"
required
/>
<TextField
fullWidth
label="Mensaje"
name="message"
multiline
rows={4}
value={formData.message}
onChange={(e) => setFormData({...formData, message: e.target.value})}
margin="normal"
required
/>
<Button
type="submit"
fullWidth
variant="contained"
size="large"
sx={{ mt: 3, mb: 2 }}
>
Enviar Mensaje
</Button>
</Box>
</Paper>
);
}

🧭 - Navigation App Bar

import {
AppBar,
Toolbar,
Typography,
Button,
IconButton,
Menu,
MenuItem,
Box,
Avatar
} from '@mui/material';
import { Menu as MenuIcon, AccountCircle } from '@mui/icons-material';
import { useState } from 'react';
function NavigationBar() {
const [anchorEl, setAnchorEl] = useState(null);
return (
<AppBar position="sticky">
<Toolbar>
<IconButton
edge="start"
color="inherit"
sx={{ mr: 2, display: { md: 'none' } }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Mi Aplicación
</Typography>
<Box sx={{ display: { xs: 'none', md: 'flex' }, gap: 2 }}>
<Button color="inherit">Inicio</Button>
<Button color="inherit">Productos</Button>
<Button color="inherit">Contacto</Button>
</Box>
<IconButton
color="inherit"
onClick={(e) => setAnchorEl(e.currentTarget)}
>
<AccountCircle />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
>
<MenuItem onClick={() => setAnchorEl(null)}>Perfil</MenuItem>
<MenuItem onClick={() => setAnchorEl(null)}>Configuración</MenuItem>
<MenuItem onClick={() => setAnchorEl(null)}>Cerrar Sesión</MenuItem>
</Menu>
</Toolbar>
</AppBar>
);
}

📚 - Stack: Componente de Layout Esencial

Stack es uno de los componentes más útiles para crear layouts flexibles y organizados:

import { Stack, Typography, Button, Avatar, Chip } from '@mui/material';
// Stack básico - vertical por defecto
function BasicStack() {
return (
<Stack spacing={2} sx={{ p: 3 }}>
<Typography variant="h4">Título Principal</Typography>
<Typography variant="body1">Contenido del párrafo</Typography>
<Button variant="contained">Acción</Button>
</Stack>
);
}

🔧 - Configuraciones de Stack

1. Dirección y Espaciado:

// Stack horizontal con diferentes espaciados
<Stack direction="row" spacing={1}>
<Chip label="Tag 1" />
<Chip label="Tag 2" />
<Chip label="Tag 3" />
</Stack>
// Stack con espaciado responsive
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={{ xs: 1, sm: 2, md: 4 }}
>
<Button variant="outlined">Opción 1</Button>
<Button variant="outlined">Opción 2</Button>
<Button variant="outlined">Opción 3</Button>
</Stack>

2. Alineación y Distribución:

// Stack con alineación personalizada
<Stack
direction="row"
spacing={2}
alignItems="center"
justifyContent="space-between"
sx={{ width: '100%', p: 2 }}
>
<Typography variant="h6">Logo</Typography>
<Stack direction="row" spacing={1}>
<Button>Login</Button>
<Button variant="contained">Sign Up</Button>
</Stack>
</Stack>

3. Dividers Automáticos:

import { Stack, Typography, Divider } from '@mui/material';
<Stack
spacing={2}
divider={<Divider orientation="horizontal" flexItem />}
sx={{ p: 2 }}
>
<Typography>Sección 1</Typography>
<Typography>Sección 2</Typography>
<Typography>Sección 3</Typography>
</Stack>
// Dividers verticales
<Stack
direction="row"
spacing={2}
divider={<Divider orientation="vertical" flexItem />}
>
<Typography>Item 1</Typography>
<Typography>Item 2</Typography>
<Typography>Item 3</Typography>
</Stack>

🛠️ - Herramientas y Recursos Útiles

🎨 - Generadores y Herramientas de Diseño

Theme Creator & Customization:

💡 - Recomendaciones

  • Utilizar sx sobre la propiedad style.
  • Realizar un diseño y configuración del Theme previamente para tener una base sólida al momento de crear componentes.
  • Crear variantes de componentes para maximizar escalabilidad.

Performance y Optimización: