Saltar al contenido principal
Los componentes de React son una forma poderosa de crear elementos interactivos y reutilizables en tu documentación.

Uso de componentes de React

Puedes crear componentes de React directamente en tus archivos MDX utilizando los hooks de React.

Ejemplo

Este ejemplo declara un componente Counter y luego lo utiliza con <Counter />.
export const Counter = () => {
  const [count, setCount] = useState(0)

  const increment = () => setCount(count + 1)
  const decrement = () => setCount(count - 1)

  return (
  <div className="flex items-center justify-center">
      <div className="flex items-center rounded-xl overflow-hidden border border-zinc-950/20 dark:border-white/20">
        <button
          onClick={decrement}
          className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-r border-zinc-950/20 dark:border-white/20"
          aria-label="Reducir"
        >
          -
        </button>

        <div className="flex text-sm items-center justify-center h-8 px-6 text-zinc-950/80 dark:text-white/80 font-medium min-w-[4rem] text-center">
          {count}
        </div>

        <button
          onClick={increment}
          className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-l border-zinc-950/20 dark:border-white/20"
          aria-label="Incrementar"
        >
          +
        </button>
      </div>
    </div>
  )
}

<Counter />
El contador se renderiza como un componente interactivo de React.

Importación de componentes

Puedes importar componentes desde tu carpeta snippets. A diferencia de React, no puedes importar componentes desde cualquier archivo MDX. Los componentes reutilizables deben referenciarse desde archivos dentro de la carpeta snippets. Obtén más información sobre fragmentos reutilizables.

Ejemplo

Este ejemplo declara un componente ColorGenerator que utiliza varios hooks de React y luego lo usa en un archivo MDX. Crea el archivo color-generator.jsx en la carpeta snippets:
/snippets/color-generator.jsx [expandible]
export const ColorGenerator = () => {
  const [hue, setHue] = useState(180)
  const [saturation, setSaturation] = useState(50)
  const [lightness, setLightness] = useState(50)
  const [colors, setColors] = useState([])

  useEffect(() => {
    const newColors = []
    for (let i = 0; i < 5; i++) {
      const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
      newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
    }
    setColors(newColors)
  }, [hue, saturation, lightness])

  const copyToClipboard = (color) => {
    navigator.clipboard
      .writeText(color)
      .then(() => {
        console.log(`¡Se copió ${color} al portapapeles!`)
      })
      .catch((err) => {
        console.error("No se pudo copiar: ", err)
      })
  }

  return (
    <div className="p-4 border dark:border-zinc-950/80 rounded-xl not-prose">
      <div className="space-y-4">
        <div className="space-y-2">
          <label className="block text-sm text-zinc-950/70 dark:text-white/70">
            Tono: {hue}°
            <input
              type="range"
              min="0"
              max="360"
              value={hue}
              onChange={(e) => setHue(Number.parseInt(e.target.value))}
              className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
              style={{
                background: `linear-gradient(to right, 
                  hsl(0, ${saturation}%, ${lightness}%), 
                  hsl(60, ${saturation}%, ${lightness}%), 
                  hsl(120, ${saturation}%, ${lightness}%), 
                  hsl(180, ${saturation}%, ${lightness}%), 
                  hsl(240, ${saturation}%, ${lightness}%), 
                  hsl(300, ${saturation}%, ${lightness}%), 
                  hsl(360, ${saturation}%, ${lightness}%))`,
              }}
            />
          </label>

          <label className="block text-sm text-zinc-950/70 dark:text-white/70">
            Saturación: {saturation}%
            <input
              type="range"
              min="0"
              max="100"
              value={saturation}
              onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
              className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
              style={{
                background: `linear-gradient(to right, 
                  hsl(${hue}, 0%, ${lightness}%), 
                  hsl(${hue}, 50%, ${lightness}%), 
                  hsl(${hue}, 100%, ${lightness}%))`,
              }}
            />
          </label>

          <label className="block text-sm text-zinc-950/70 dark:text-white/70">
            Luminosidad: {lightness}%
            <input
              type="range"
              min="0"
              max="100"
              value={lightness}
              onChange={(e) => setLightness(Number.parseInt(e.target.value))}
              className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
              style={{
                background: `linear-gradient(to right, 
                  hsl(${hue}, ${saturation}%, 0%), 
                  hsl(${hue}, ${saturation}%, 50%), 
                  hsl(${hue}, ${saturation}%, 100%))`,
              }}
            />
          </label>
        </div>

        <div className="flex space-x-1">
          {colors.map((color, idx) => (
            <div
              key={idx}
              className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
              style={{ backgroundColor: color }}
              title={`Haz clic para copiar: ${color}`}
              onClick={() => copyToClipboard(color)}
            />
          ))}
        </div>

        <div className="text-sm font-mono text-zinc-950/70 dark:text-white/70">
          <p>
            Color base: hsl({hue}, {saturation}%, {lightness}%)
          </p>
        </div>
      </div>
    </div>
  )
}
Importa el componente ColorGenerator y utilízalo en un archivo MDX:
import { ColorGenerator } from "/snippets/color-generator.jsx"

<ColorGenerator />
El generador de colores se renderiza como un componente interactivo de React.

Consideraciones

Los componentes con hooks de React se renderizan del lado del cliente, lo que tiene varias implicaciones:
  • SEO: Es posible que los motores de búsqueda no indexen por completo el contenido dinámico.
  • Carga inicial: Los visitantes pueden ver un destello de contenido de carga antes de que se rendericen los componentes.
  • Accesibilidad: Asegúrate de que los cambios de contenido dinámico se anuncien a los lectores de pantalla.
  • Optimiza los arreglos de dependencias: Incluye solo las dependencias necesarias en los arreglos de dependencias de useEffect.
  • Memoiza cálculos complejos: Usa useMemo o useCallback para operaciones costosas.
  • Reduce las re-renderizaciones: Divide los componentes grandes en otros más pequeños para evitar re-renderizaciones en cascada.
  • Carga diferida (lazy loading): Considera cargar de forma diferida los componentes complejos para mejorar el tiempo de carga inicial de la página.