BackVolver al blog

Estilización en Next.js con Styled JSX

Styled JSX es una biblioteca CSS-in-JS que permite escribir CSS encapsulado y con alcance limitado para estilizar componentes. Esta publicación te ayudará a comenzar a usar Styled JSX en Next.js.

Styled JSX es una biblioteca CSS-in-JS que permite escribir CSS encapsulado y con alcance limitado para estilizar componentes. Los estilos que introduces para un componente no afectarán a otros componentes, permitiéndote añadir, cambiar y eliminar estilos sin preocuparte por efectos secundarios no deseados.

Empezando

Next.js incluye Styled JSX por defecto, por lo que comenzar es tan simple como añadir una etiqueta <style jsx> a un elemento React existente y escribir CSS dentro de ella:

pages/index.js
function Home() {
  return (
    <div className="container">
      <h1>Hello Next.js</h1>
      <p>Let's explore different ways to style Next.js apps</p>
      <style jsx>{`
        .container {
          margin: 50px;
        }
        p {
          color: blue;
        }
      `}</style>
    </div>
  );
}
 
export default Home;

En este ejemplo, incluimos estilos para el elemento contenedor del componente y un párrafo. Aunque estamos usando selectores genéricos, los estilos no afectan a elementos con el nombre de clase container o etiquetas <p> en otros componentes. Esto se debe a que Styled JSX asegura que los estilos estén limitados solo a este componente (aplicando nombres de clase únicos adicionales a los elementos estilizados).

Al añadir solo un atributo jsx a un elemento <style>, puedes escribir CSS estándar que se auto-prefija y se limita automáticamente al componente. Los elementos <style jsx> deben colocarse dentro del elemento raíz del componente.

Añadiendo estilos globales

La mayoría de los proyectos necesitan algunos estilos globales para estilizar el elemento body o proporcionar resets de CSS. Styled JSX nos permite añadir estilos globales usando <style jsx global>. Por ejemplo:

pages/index.js
function Home() {
  return (
    <div className="container">
      <h1>Hello Next.js</h1>
      <p>Let's explore different ways to style Next.js apps</p>
      <style jsx>{`
        .container {
          margin: 50px;
        }
        p {
          color: blue;
        }
      `}</style>
      <style jsx global>{`
        p {
          font-size: 20px;
        }
      `}</style>
    </div>
  );
}
 
export default Home;

Esto aplica un tamaño de fuente de 20px a todas las etiquetas <p> en esta página específica.

Para aplicar estilos globales a todas las páginas de nuestra aplicación, un buen enfoque es crear primero un componente de diseño (layout) con los estilos globales, luego envolver todas las páginas con él.

Usar un componente de diseño proporciona la flexibilidad de aplicar un conjunto de estilos a algunas páginas mientras permite un estilo diferente para otras:

components/Layout.js
function Layout(props) {
  return (
    <div className="page-layout">
      {props.children}
      <style jsx global>{`
        body {
          margin: 0;
          padding: 0;
          font-size: 18px;
          font-weight: 400;
          line-height: 1.8;
          color: #333;
          font-family: sans-serif;
        }
        h1 {
          font-weight: 700;
        }
        p {
          margin-bottom: 10px;
        }
      `}</style>
    </div>
  );
}
 
export default Layout;

En Next.js, podemos cargar el diseño una vez para todas las páginas creando un componente App personalizado dentro de pages/_app.js, importando el componente Layout y luego añadiéndolo al método render de la siguiente manera:

pages/_app.js
import React from 'react';
import App, { Container } from 'next/app';
import Layout from '../components/Layout';
 
class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;
 
    return (
      <Container>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </Container>
    );
  }
}
 
export default MyApp;

Escribiendo estilos en archivos externos

También podemos escribir estilos en archivos externos fuera del componente.

Por ejemplo, podemos mover nuestros estilos globales del componente Layout a un archivo separado de la siguiente manera:

styles/global.js
import css from 'styled-jsx/css';
 
export default css.global`
  body {
    margin: 0;
    padding: 0;
    font-size: 18px;
    font-weight: 400;
    line-height: 1.8;
    color: #333;
    font-family: sans-serif;
  }
  h1 {
    font-weight: 700;
  }
  p {
    margin-bottom: 10px;
  }
`;

Luego podemos importar los estilos de vuelta al componente Layout:

components/Layout.js
import globalStyles from '../styles/global.js';
 
function Layout(props) {
  return (
    <div className="page-layout">
      {props.children}
      <style jsx global>
        {globalStyles}
      </style>
    </div>
  );
}
 
export default Layout;

Selectores globales puntuales

Los estilos que añadimos a un componente usando <style jsx> afectan solo a los elementos dentro de ese componente, pero no a los componentes hijos.

A veces, podemos necesitar anular un cierto estilo de un componente hijo. Para esto, Styled JSX proporciona :global(), dando acceso a selectores globales puntuales.

Por ejemplo, digamos que tenemos un componente <Widget> que contiene un botón con el nombre de clase btn. Si queremos cambiar los colores de este botón solo cuando el widget se importa en la página de inicio, podemos hacerlo así:

pages/index.js
import Widget from '../components/Widget';
 
function Home() {
  return (
    <div className="container">
      <h1>Hello Next.js</h1>
      <Widget />
      <style jsx>{`
        .container {
          margin: 50px;
        }
        .container :global(.btn) {
          background: #000;
          color: #fff;
        }
      `}</style>
    </div>
  );
}
 
export default Home;

Estilos dinámicos

En comparación con otras soluciones, la capacidad de adaptar el estilo de un componente basado en sus props es una gran ventaja de las bibliotecas CSS-in-JS.

Con Styled JSX podemos hacerlo así:

components/Alert.js
function Alert(props) {
  return (
    <div className="alert">
      {props.children}
      <style jsx>{`
        .alert {
          display: inline-block;
          padding: 20px;
          border-radius: 8px;
        }
      `}</style>
      <style jsx>{`
        .alert {
          background: ${props.type == 'warning' ? '#fff3cd' : '#eee'};
        }
      `}</style>
    </div>
  );
}
 
export default Alert;

Si al componente Alert se le pasa una prop type con un valor warning como:

<Alert type="warning">This is an important message</Alert>

el componente tendrá un fondo naranja. Sin especificar la prop type, el fondo volvería al color gris predeterminado.

Nota que colocamos el estilo dinámico en una etiqueta <style jsx> separada. Esto no es requerido, pero es recomendado separar estilos estáticos y dinámicos para que solo las partes dinámicas se recalculen cuando cambien las props.

Un enfoque alternativo para adaptar estilos basados en props es aplicar diferentes nombres de clase basados en el valor de la prop como se muestra a continuación:

components/Alert.js
function Alert(props) {
  return (
    <div className={props.type == 'warning' ? 'alert warning' : 'alert'}>
      {props.children}
      <style jsx>{`
        .alert {
          display: inline-block;
          padding: 20px;
          border-radius: 8px;
          background: #eee;
        }
        .alert.warning {
          background: #fff3cd;
        }
      `}</style>
    </div>
  );
}
 
export default Alert;

Creando un tema para el sitio

Un tema puede ser un objeto simple donde incluimos las variables más comunes que podríamos necesitar en nuestra aplicación:

styles/theme.js
const theme = {
  fontFamily: {
    sansSerif: '-apple-system, "Helvetica Neue", Arial, sans-serif',
    mono: 'Menlo, Monaco, monospace',
  },
  colors: {
    text: '#333',
    background: '#fff',
    link: '#1eaaf1',
    linkHover: '#0d8ecf',
    border: '#ddd',
    warning: '#fff3cd',
    success: '#d4edda',
  },
};
 
export default theme;

Luego importamos este archivo de tema en nuestros componentes y reemplazamos valores hardcodeados con variables:

components/Layout.js
import theme from '../styles/theme';
 
function Layout(props) {
  return (
    <div className="page-wrapper">
      {props.children}
      <style jsx global>{`
        body {
          background: ${theme.colors.background};
          color: ${theme.colors.text};
          font-family: ${theme.fontFamily.sansSerif};
        }
      `}</style>
      <style jsx global>{`
        body {
          margin: 0;
          padding: 0;
          font-size: 18px;
          font-weight: 400;
          line-height: 1.8;
        }
        h1 {
          font-weight: 700;
        }
        p {
          margin-bottom: 10px;
        }
      `}</style>
    </div>
  );
}
 
export default Layout;
components/Alert.js
import theme from '../styles/theme';
 
function Alert(props) {
  return (
    <div className="alert">
      {props.children}
      <style jsx>{`
        .alert {
          display: inline-block;
          padding: 20px;
          border-radius: 8px;
        }
      `}</style>
      <style jsx>{`
        .alert {
          background: ${props.type == 'warning'
            ? theme.colors.warning
            : theme.colors.light};
        }
      `}</style>
    </div>
  );
}
 
export default Alert;

En esta publicación, cubrimos cómo comenzar con Styled JSX. Para aprender más sobre características adicionales, asegúrate de consultarlo en GitHub.