Qué es JSX y por qué se usa
JSX es una sintaxis declarativa que te permite describir la interfaz (UI) usando una forma muy parecida a HTML, pero dentro de JavaScript. No es HTML real: es una extensión de sintaxis que, al compilarse, se convierte en llamadas a funciones de React que crean elementos. La idea práctica es que escribes “cómo debería verse” la UI en función de datos y estado, y React se encarga de actualizar lo necesario.
Dos ideas clave para trabajar con JSX con seguridad: (1) dentro de JSX puedes incrustar expresiones de JavaScript usando llaves {}; (2) los “atributos” que ves en JSX a veces se parecen a HTML, pero siguen reglas de JavaScript y del API de React.
JSX y JavaScript: expresiones e interpolación
Expresiones dentro de llaves
Dentro de {} puedes poner cualquier expresión (algo que produce un valor): variables, ternarios, llamadas a funciones, map, etc. No puedes poner sentencias como if, for o switch directamente (porque no son expresiones). Para esas situaciones, usa ternarios, operadores lógicos o calcula el valor antes del return.
function Welcome({ user }) { const fullName = user.firstName + " " + user.lastName; return ( <h2>Hola, {fullName}!</h2> );}Condicionales comunes
function Status({ isOnline }) { return ( <p> Estado: {isOnline ? "Conectado" : "Desconectado"} </p> );}function Notifications({ count }) { return ( <div> <h3>Bandeja</h3> {count > 0 && <p>Tienes {count} nuevas</p>} </div> );}Listas con map
Una lista en UI suele venir de un array. En JSX, lo típico es transformar el array en elementos con map. Recuerda que cada elemento de una lista debe tener una key estable.
function TodoList({ todos }) { return ( <ul> {todos.map((t) => ( <li key={t.id}>{t.title}</li> ))} </ul> );}Atributos en JSX: diferencias con HTML y reglas frecuentes
Un solo elemento raíz
Un componente debe devolver un único “nodo raíz”. Si necesitas varios hermanos, envuélvelos en un contenedor o usa un fragmento.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
// ❌ Incorrecto: dos elementos raízreturn ( <h2>Título</h2> <p>Texto</p>);// ✅ Opción 1: contenedorreturn ( <div> <h2>Título</h2> <p>Texto</p> </div>);// ✅ Opción 2: fragmentoreturn ( <> <h2>Título</h2> <p>Texto</p> </>);camelCase en propiedades y eventos
En JSX, muchas propiedades siguen la convención de JavaScript: camelCase. Esto se nota especialmente en eventos y en algunas propiedades del DOM.
- Eventos:
onClick,onChange,onSubmit(noonclick). - Propiedades:
tabIndex,readOnly,maxLength.
function Button() { function handleClick() { console.log("click"); } return ( <button type="button" onClick={handleClick}> Guardar </button> );}class vs className
En JSX no se usa class (palabra reservada en JavaScript). Se usa className.
function Card() { return ( <section className="card"> <h3 className="card__title">Producto</h3> </section> );}Estilos inline: objeto en lugar de string
El atributo style recibe un objeto JavaScript, no una cadena. Las propiedades CSS se escriben en camelCase (por ejemplo, backgroundColor).
function Price({ value }) { const isHigh = value > 100; return ( <p style={{ color: isHigh ? "crimson" : "seagreen", fontWeight: 600, marginTop: 8, }} > ${value} </p> );}Atributos booleanos
Si un atributo booleano es true, puedes escribirlo sin valor. Si depende de una expresión, usa llaves.
function Form({ isDisabled }) { return ( <input type="text" disabled={isDisabled} readOnly /> );}HTML vs props: misma forma, distinto significado
En JSX puedes usar etiquetas HTML (como <div>, <input>) y también componentes (como <UserCard />). La diferencia práctica: en etiquetas HTML, los atributos se traducen a propiedades del DOM; en componentes, esos “atributos” se convierten en props que tú defines.
function UserCard({ name, isPro }) { return ( <article className={isPro ? "pro" : "basic"}> <h3>{name}</h3> </article> );}function App() { return ( <div> {/* name e isPro son props para UserCard */} <UserCard name="Ana" isPro={true} /> {/* className es una prop del DOM para un div */} <div className="separator" /> </div> );}Guía práctica: convertir una UI deseada a JSX
Una forma efectiva de aprender JSX es partir de una UI “en palabras” o un boceto y convertirlo a estructura y expresiones.
Paso a paso (método recomendado)
- 1) Identifica el contenedor raíz que agrupa todo lo que vas a devolver.
- 2) Escribe la estructura estática (etiquetas y jerarquía) sin datos dinámicos.
- 3) Marca qué partes son dinámicas (texto, clases, estilos, atributos, listas).
- 4) Sustituye esas partes por expresiones con
{}(variables, ternarios,map). - 5) Revisa reglas:
className,stylecomo objeto,camelCase,keyen listas.
Ejemplo 1: “Tarjeta de perfil”
UI deseada: una tarjeta con avatar, nombre, y un badge “PRO” si el usuario es premium. Si no hay avatar, mostrar un círculo con iniciales.
1) Estructura base:
function ProfileCard() { return ( <article> <div></div> <div> <h3></h3> </div> </article> );}2) Dinamismo con expresiones:
function ProfileCard({ user }) { const initials = (user.name || "?") .split(" ") .slice(0, 2) .map((p) => p[0]?.toUpperCase()) .join(""); return ( <article className="profile"> <div className="profile__avatar"> {user.avatarUrl ? ( <img src={user.avatarUrl} alt={user.name} /> ) : ( <span aria-label="Iniciales">{initials}</span> )} </div> <div className="profile__info"> <h3>{user.name}</h3> {user.isPro && <span className="badge">PRO</span>} </div> </article> );}Ejemplo 2: “Tabla de productos con estado visual”
UI deseada: tabla con nombre, stock y una marca visual en rojo si el stock es 0. Mostrar “Sin productos” si la lista está vacía.
function ProductTable({ products }) { if (products.length === 0) { return <p>Sin productos</p>; } return ( <table> <thead> <tr> <th>Nombre</th> <th>Stock</th> </tr> </thead> <tbody> {products.map((p) => ( <tr key={p.id}> <td>{p.name}</td> <td style={{ color: p.stock === 0 ? "crimson" : "inherit", fontWeight: p.stock === 0 ? 700 : 400, }} > {p.stock === 0 ? "Agotado" : p.stock} </td> </tr> ))} </tbody> </table> );}Ejemplo 3: “Formulario controlado visualmente (atributos y eventos)”
UI deseada: input de email, botón deshabilitado si el email está vacío, y un mensaje de error si no contiene @.
function EmailForm() { const [email, setEmail] = React.useState(""); const isEmpty = email.trim() === ""; const isInvalid = !isEmpty && !email.includes("@"); function handleSubmit(e) { e.preventDefault(); alert("Enviado: " + email); } return ( <form onSubmit={handleSubmit}> <label> Email <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} aria-invalid={isInvalid} /> </label> {isInvalid && ( <p style={{ color: "crimson" }}>Debe incluir @</p> )} <button type="submit" disabled={isEmpty}> Enviar </button> </form> );}Fragmentos: agrupar sin añadir nodos al DOM
Los fragmentos te permiten devolver varios elementos sin envolverlos en un <div>. Úsalos cuando el contenedor extra afecte estilos, layout o semántica.
function ListWithSeparator({ items }) { return ( <ul> {items.map((item, idx) => ( <React.Fragment key={item.id}> <li>{item.label}</li> {idx < items.length - 1 && <hr />} </React.Fragment> ))} </ul> );}Nota: si necesitas key en un fragmento dentro de un map, usa <React.Fragment key=...> (la forma corta <></> no acepta atributos).
Errores típicos en JSX: diagnóstico y corrección
| Problema | Síntoma / error | Causa | Corrección |
|---|---|---|---|
| Llaves mal ubicadas en atributos | Unexpected token o el valor no se aplica | Mezclar comillas y llaves incorrectamente | Si es string literal: comillas. Si es expresión: llaves. |
Usar class en lugar de className | Warning o estilos no aplican como esperas | class no es el atributo correcto en JSX | Usa className. |
| Style como string | Warning o estilos ignorados | style espera un objeto | Usa style={{ color: "red" }}. |
| Renderizar objetos directamente | Objects are not valid as a React child | Intentas imprimir un objeto en JSX | Renderiza una propiedad, conviértelo a string o mapea a elementos. |
| Olvidar el elemento raíz | Adjacent JSX elements must be wrapped | Devuelves hermanos sin wrapper | Envuelve en <div> o fragmento. |
1) Llaves y comillas: patrones correctos
Regla práctica: comillas para literales string; llaves para expresiones.
// ✅ String literal<input placeholder="Tu nombre" />// ✅ Expresión (variable)<input placeholder={placeholderText} />// ✅ Expresión (template string)<input placeholder={`${first} ${last}`} />// ❌ Incorrecto: llaves dentro de comillas<input placeholder="{placeholderText}" />2) Renderizar objetos: cómo detectarlo y arreglarlo
Si ves Objects are not valid as a React child, revisa qué estás poniendo entre llaves. Ejemplo típico:
function Debug({ user }) { return <p>Usuario: {user}</p>; // ❌ user es un objeto}Correcciones comunes:
// ✅ Renderizar una propiedadreturn <p>Usuario: {user.name}</p>;// ✅ Convertir a string (para depuración)return <pre>{JSON.stringify(user, null, 2)}</pre>;3) Confundir sentencias con expresiones
Esto falla porque if no es una expresión dentro de JSX:
function Example({ ok }) { return ( <div> {if (ok) { <p>OK</p> }} </div> );}Alternativas correctas:
function Example({ ok }) { return ( <div> {ok ? <p>OK</p> : <p>No OK</p>} </div> );}function Example({ ok }) { return ( <div> {ok && <p>OK</p>} </div> );}4) Atributos del DOM vs props: confusiones frecuentes
- En DOM:
onClickespera una función. Si escribesonClick={handleClick()}, ejecutas la función al renderizar (casi siempre es un bug). - En componentes: puedes pasar cualquier tipo de dato como prop (string, número, boolean, objeto, función), pero recuerda que al renderizar debes convertirlo a algo renderizable (texto o elementos).
function App() { function handleClick() { console.log("clicked"); } return ( <> {/* ✅ pasar la función */} <button onClick={handleClick}>Click</button> {/* ❌ se ejecuta al renderizar */} {/* <button onClick={handleClick()}>Click</button> */} </> );}