---
title: Componentes React personalizados
description: Puedes definir componentes React personalizados directamente en tus archivos MDX, lo que te da control total sobre la presentación de tu contenido.
---

Define componentes React personalizados directamente en archivos MDX. Exporta un componente de función de flecha al principio del archivo y úsalo en línea. Los componentes se renderizan en el servidor durante el build con acceso a todos los componentes integrados de Jamdesk y las clases de Tailwind.

Las capturas de pantalla muestran la interfaz en inglés.

## Uso básico

Define un componente usando `export const` al principio de tu archivo MDX:

```mdx
export const Highlight = ({ children, color }) => (
  <span style={{ backgroundColor: color, padding: '0.2em 0.4em', borderRadius: '4px' }}>
    {children}
  </span>
);

This text has a <Highlight color="#ffeb3b">yellow highlight</Highlight> in it.
```

<Note>
Los nombres de los componentes deben usar PascalCase (comenzar con una letra mayúscula).
</Note>

## Ejemplo: tarjeta hero

Aquí hay un ejemplo más complejo con múltiples props y clases de Tailwind:

```mdx
export const HeroCard = ({ title, description, href, icon }) => (
  <a
    href={href}
    className="group block p-6 rounded-lg border border-gray-200 hover:border-blue-500 transition-colors"
  >
    <div className="flex items-center gap-3 mb-2">
      <Icon icon={icon} />
      <h3 className="font-semibold text-lg">{title}</h3>
    </div>
    <p className="text-gray-600">{description}</p>
  </a>
);

<div className="grid grid-cols-2 gap-4">
  <HeroCard
    title="Getting started"
    description="Learn the basics"
    href="/quickstart"
    icon="rocket"
  />
  <HeroCard
    title="Components"
    description="Explore available components"
    href="/components/overview"
    icon="puzzle-piece"
  />
</div>
```

## Usar componentes integrados

Tus componentes personalizados tienen acceso a todos los componentes integrados de Jamdesk. No se necesitan imports:

```mdx
export const FeatureCard = ({ title, children }) => (
  <Card title={title} icon="star">
    {children}
    <Tip>This tip is inside a custom component!</Tip>
  </Card>
);

<FeatureCard title="My Feature">
  <Note>Notes work inside custom components too!</Note>
</FeatureCard>
```

Componentes integrados por categoría:

| Categoría | Componentes |
|----------|-----------|
| Llamadas | `Note`, `Tip`, `Info`, `Warning`, `Check`, `Danger` |
| Diseño | `Card`, `Columns`, `Tabs`, `Tab`, `Accordion`, `Steps`, `Step`, `View` |
| Medios | `Frame`, `Icon`, `Badge`, `Tooltip` |
| Código | `CodeGroup` |
| Docs de API | `ParamField`, `ResponseField`, `Expandable` |

## Limitaciones

- **Solo funciones de flecha**: Usa la sintaxis `export const`. La sintaxis `export function` no es compatible.
- **Sin imports**: No puedes importar paquetes externos ni otros archivos
- **Sin hooks**: Los hooks de React (`useState`, `useEffect`, `useRef`, etc.) no están disponibles. Los componentes se renderizan en el servidor durante el build, y los hooks solo funcionan en el cliente. Para componentes interactivos, usa archivos de snippets en su lugar (ver solución de problemas a continuación).
- **Solo servidor**: Los componentes se renderizan en el servidor; no hay interactividad del lado del cliente ni controladores de eventos
- **Solo Tailwind**: Usa clases de Tailwind CSS para estilos; CSS-in-JS no está disponible
- **Sintaxis estricta**: Los errores de sintaxis en los componentes en línea fallarán el build con un mensaje de error claro
- **Nombres en PascalCase**: Los nombres de los componentes deben comenzar con una letra mayúscula y contener solo letras y números (sin guiones bajos)

<Warning>
**¿Necesitas hooks de React?** Los componentes en línea no pueden usar `useState`, `useEffect` u otros hooks porque se renderizan en el servidor durante el build. Para componentes interactivos que necesiten estado o efectos, crea un archivo de snippet con la directiva `'use client'` (ver la sección de solución de problemas a continuación).
</Warning>

## Mejores prácticas

<Steps>
<Step title="Mantén los componentes simples">
Los componentes en línea deben ser presentacionales. Para lógica compleja, solicita un componente integrado.
</Step>
<Step title="Usa HTML semántico">
Asegúrate de que tus componentes sean accesibles con los elementos HTML adecuados y atributos ARIA.
</Step>
<Step title="Prueba exhaustivamente">
Previsualiza tu documentación localmente para verificar que los componentes se rendericen correctamente en los modos claro y oscuro.
</Step>
</Steps>

## Solución de problemas

<Accordion title="Error de componente no encontrado">
**Error:** `Expected component 'MyComponent' to be defined`

**Causas:**
- El nombre del componente no usa PascalCase (debe comenzar con letra mayúscula)
- Error de sintaxis en la definición del componente
- Etiqueta de cierre o paréntesis faltante

**Solución:** Verifica que tu export siga este patrón:
```mdx
export const MyComponent = ({ prop }) => (
  <div>{prop}</div>
);
```
</Accordion>

<Accordion title="El build falla con error de sintaxis">
**Error:** El build falla mencionando Babel o sintaxis JSX

**Causas:**
- Sintaxis JSX inválida en tu componente
- Etiquetas o corchetes sin cerrar
- Uso de características de JavaScript no compatibles

**Solución:** Verifica que tu JSX sea válido. Problemas comunes:
- Todas las etiquetas deben cerrarse (`<img />` no `<img>`)
- Usa `className` en lugar de `class`
- Envuelve múltiples elementos en un fragmento `<>...</>` o un elemento padre
</Accordion>

<Accordion title="Advertencia: el componente reemplaza un integrado">
**Advertencia:** `Inline component(s) override built-in: Note`

**Causa:** Tu componente en línea tiene el mismo nombre que un componente integrado.

**Solución:** Renombra tu componente para evitar conflictos:
```mdx
// Instead of: export const Note = ...
export const CustomNote = ({ children }) => (
  <div className="my-note">{children}</div>
);
```
</Accordion>

<Accordion title="Los estilos no se aplican">
**Problema:** Las clases de Tailwind no funcionan

**Causas:**
- Uso de CSS-in-JS u objetos de estilo en línea (soporte limitado)
- La clase de Tailwind no está incluida en el build

**Solución:** Usa clases de utilidad estándar de Tailwind. Para estilos personalizados, usa la prop `style` en línea con valores simples:
```mdx
export const Highlight = ({ children }) => (
  <span style={{ backgroundColor: '#ffeb3b' }}>{children}</span>
);
```
</Accordion>

<Accordion title="El componente no se renderiza (export function)">
**Problema:** El componente definido con `export function` no se renderiza

**Causa:** Solo se admite la sintaxis de función de flecha `export const`.

**Solución:** Convierte tu función a sintaxis de función de flecha:
```mdx
// Instead of:
export function MyComponent({ prop }) {
  return <div>{prop}</div>;
}

// Use:
export const MyComponent = ({ prop }) => (
  <div>{prop}</div>
);
```
</Accordion>

<Accordion title="El nombre del componente tiene caracteres inválidos">
**Problema:** El componente con guiones bajos o caracteres especiales no funciona

**Causa:** Los nombres de los componentes deben ser identificadores PascalCase válidos (solo letras y números).

**Solución:** Usa solo letras y números en el nombre de tu componente:
```mdx
// Instead of: export const Hero_Card = ...
// Instead of: export const my-component = ...
export const HeroCard = ({ title }) => (
  <div>{title}</div>
);
```
</Accordion>

<Accordion title="useState no está definido (o useEffect, useRef, etc.)">
**Error:** `ReferenceError: useState is not defined`

**Causa:** Los hooks de React no están disponibles en los componentes en línea. Los componentes en línea se renderizan en el servidor durante el proceso de build, y los hooks solo funcionan en componentes React del lado del cliente.

**Solución:** Para componentes interactivos que necesiten estado o efectos, crea un archivo de snippet en su lugar:

1. Crea un archivo en tu directorio `/snippets` (p. ej., `/snippets/counter.tsx`)
2. Agrega `'use client'` al principio del archivo
3. Impórtalo y úsalo en tu MDX

```tsx title="/snippets/counter.tsx"
'use client';

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}
```

```mdx title="your-page.mdx"
import { Counter } from '/snippets/counter';

<Counter />
```
</Accordion>

## ¿Qué sigue?

<Columns cols={2}>
  <Card title="Snippets" icon="scissors" href="/es/content/snippets">
    Componentes reutilizables entre páginas, incluyendo los interactivos con hooks
  </Card>
  <Card title="Descripción general de componentes" icon="puzzle-piece" href="/es/components/overview">
    Explora los componentes integrados
  </Card>
</Columns>
