Saltar al contenido principal
Los componentes de React son una forma potente 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 usando hooks de React.

Ejemplo

Este ejemplo declara un componente Counter y luego lo usa 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 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. 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
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 color se muestra 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 “flash” 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.
  • Memoiza cálculos complejos: Usa useMemo o useCallback para operaciones costosas.
  • Reduce los re-renderizados: Divide 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