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 construir componentes de React directamente en tus archivos MDX con React hooks.

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="Disminuir"
        >
          -
        </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="Aumentar"
        >
          +
        </button>
      </div>
    </div>
  )
}

<Counter />
El contador se representa 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 hacer referencia a 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 emplea en un archivo MDX. Crea el archivo color-generator.jsx en la carpeta snippets:
/snippets/color-generator.jsx
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(`${color} copiado al portapapeles`)
      })
      .catch((err) => {
        console.error("Error al 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={`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 úsalo 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 que usan hooks de React se renderizan en el 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 parpadeo de contenido de carga antes de que se rendericen los componentes.
  • Accesibilidad: Asegúrate de que los cambios en el contenido dinámico se anuncien a los lectores de pantalla.
  • Optimiza los arrays de dependencias: Incluye solo las dependencias necesarias en tus arrays de dependencias de useEffect.
  • Memoriza cálculos complejos: Usa useMemo o useCallback para operaciones costosas.
  • Reduce los re-renderizados: Divide los componentes grandes en otros más pequeños para evitar re-renderizados 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.
I