Controlar la apariencia de un componente según la interacción del usuario es una necesidad constante en el desarrollo front end. Saber aplicar estilos dinámicos de forma limpia y profesional marca la diferencia entre un código mantenible y uno difícil de escalar. A continuación se explica paso a paso cómo lograrlo en Next.js utilizando la librería ClassNames junto con CSS Modules.
¿Qué son los estilos dinámicos y por qué importan en Next.js?
Los estilos dinámicos permiten que un componente cambie su apariencia en respuesta a una acción del usuario, como hacer clic en un botón. El ejemplo planteado es sencillo pero representativo: una imagen que, al recibir un clic, muestra u oculta un borde de color rosa [0:12].
Para que esto funcione en Next.js hay que convertir el componente de servidor a cliente, ya que se necesita manejar estado. Esto se logra agregando la directiva correspondiente y utilizando el hook useState de React [0:31].
jsx
const [hasBorder, setBorder] = useState(false);
El estado hasBorder almacena un valor booleano que indica si el borde debe mostrarse. La función setBorder lo alterna cada vez que el usuario hace clic, implementando un comportamiento tipo switch [0:47].
¿Cómo se implementa el evento de clic en React?
Se crea una función handleClick que niega el valor actual del estado. Luego se asigna al atributo onClick del botón [0:55].
jsx
const handleClick = () => {
setBorder(!hasBorder);
};
Un console.log(hasBorder) permite verificar en la consola del navegador que el estado cambia correctamente entre true y false con cada clic [1:20].
¿Cómo instalar y usar ClassNames para clases condicionales?
ClassNames es una librería ligera que lleva años siendo utilizada a nivel profesional. Se instala con un solo comando [1:40]:
bash
npm i classnames
Su función principal es generar cadenas de clases CSS de forma condicional. Se le pasan nombres de clase y, opcionalmente, una condición booleana que determina si esa clase se aplica o no.
jsx
const buttonStyles = classNames('button', {
'button-bordered': hasBorder,
});
Cuando hasBorder es true, la clase button-bordered se incluye en el resultado; cuando es false, se omite [2:30].
¿Cómo organizar las importaciones en un componente?
Una buena práctica mencionada es ordenar las dependencias de la siguiente manera [2:20]:
- Dependencias externas primero (como
classnames).
- Estados y hooks de React después.
- Componentes internos a continuación.
- Estilos al final.
Este estándar facilita la lectura y el mantenimiento del código.
¿Qué es el bind de ClassNames y por qué es necesario con CSS Modules?
Next.js utiliza CSS Modules, lo que significa que cada clase recibe un hash único para evitar colisiones de nombres. El problema es que ClassNames, por defecto, trabaja con cadenas de texto planas que no coinciden con esos nombres transformados [2:50].
La solución es usar el método bind que provee la librería. Este método une el objeto de estilos importado desde el módulo CSS con la lógica de ClassNames [3:10].
jsx
import classNames from 'classnames/bind';
import stylesDescription from './Description.module.css';
const cx = classNames.bind(stylesDescription);
Ahora, en lugar de escribir cadenas manuales, se usan los nombres de clase tal como están definidos en el archivo CSS. La variable cx (o context, como se nombra en el ejemplo) reemplaza a className tradicional [3:20].
jsx
const buttonStyles = cx('button', {
border: hasBorder,
});
¿Cómo aplicar los estilos al elemento correcto?
Un detalle importante: si el estilo de borde se aplica al botón pero la imagen está encima de él, el borde no será visible. La solución es asignar buttonStyles directamente al elemento <img> en lugar del <button> [4:10].
Los estilos del módulo CSS quedan así:
css
.button {
background: transparent;
border: none;
cursor: pointer;
}
.border {
border: 3px solid var(--main-color);
}
El modificador .border solo se activa cuando el estado hasBorder es true, logrando que la imagen muestre u oculte su borde de forma completamente dinámica.
Si ya usas CSS Modules en tus proyectos con Next.js, integrar ClassNames con bind es el siguiente paso natural para manejar estilos condicionales sin ensuciar tu JSX con ternarios. ¿Has probado otras formas de gestionar estilos dinámicos? Comparte tu experiencia en los comentarios.