
HTML a JSX: Los 7 errores que comete todo principiante de React
📷 Pexels / PexelsHTML a JSX: Los 7 errores que comete todo principiante de React
className, htmlFor, etiquetas autocerrantes, estilos en linea - la conversion de HTML a JSX esta llena de trampas sutiles. Aprendelos una vez y nunca mas los depures.
Hay un rito de iniciacion que le ocurre a casi todo desarrollador que aprende React. Tienes un buen fragmento de HTML, quizas de una entrega de diseno, una plantilla estatica, o algo que copiaste de un proyecto antiguo, y lo pones directamente en un componente. Luego los errores empiezan.
Warning: Invalid DOM property `class`. Did you mean `className`?
Luego otro.
Warning: Invalid DOM property `for`. Did you mean `htmlFor`?
Luego posiblemente un error de analisis de una etiqueta <img> o <input> sin cerrar. Para cuando has desplazado seis advertencias, te preguntas por que React no puede simplemente aceptar HTML normal. Una pregunta valida, en realidad.
Por que JSX no es solo HTML
La respuesta corta: JSX parece HTML pero es JavaScript disfrazado. Cuando Babel procesa tu componente, cada <div className="wrapper"> se convierte en React.createElement('div', { className: 'wrapper' }). Esa transformacion es lo que hace JSX tan poderoso, pero tambien es por que no puedes copiar HTML literalmente.
HTML es un lenguaje de marcado. Tiene sus propias reglas de analisis, y los navegadores estan disenados para ser indulgentes con el. JSX es sintaxis JavaScript que resulta parecerse a HTML. Los analizadores de JavaScript no son indulgentes. Tienen palabras clave reservadas, reglas de sintaxis estrictas y ninguna tolerancia para la ambiguedad.
Esa tension: la flexibilidad de HTML frente a la rigurosidad de JavaScript, es la causa raiz de cada error de conversion que encontraras.
Las transformaciones que necesitas conocer
1. class → className
Este es el que todos aprenden primero, generalmente por una advertencia.
<!-- HTML -->
<div class="container main-content">
<p class="text-gray">Hello world</p>
</div>
// JSX
<div className="container main-content">
<p className="text-gray">Hello world</p>
</div>
La razon es simple: class es una palabra clave reservada en JavaScript (es como defines clases ES6). Si JSX permitiera class como atributo, el analizador de JavaScript se confundiria. className es una solucion provisional que mapea directamente a la propiedad DOM del mismo nombre: cuando React renderiza al DOM real, establece la propiedad className, que corresponde al atributo HTML class. El resultado final es identico; solo cambia la sintaxis.
2. for → htmlFor
La misma logica. for es una palabra clave reservada en JavaScript (es como escribes bucles for). Las etiquetas en los formularios usan for para conectarse al id de una entrada:
<!-- HTML -->
<label for="email">Email address</label>
<input type="email" id="email" />
// JSX
<label htmlFor="email">Email address</label>
<input type="email" id="email" />
Muchos desarrolladores recuerdan className pero olvidan htmlFor. Se discute menos pero lanzara el mismo tipo de advertencia si lo omites.
3. Elementos void autocerrantes
En HTML, los elementos void: etiquetas que no pueden tener hijos, no necesitan un slash de cierre. Los navegadores lo aceptan bien:
<!-- Valid HTML -->
<img src="photo.jpg" alt="A photo">
<input type="text" name="username">
<br>
<hr>
<meta charset="UTF-8">
JSX requiere que cada elemento se cierre explicitamente. O agregas el slash de cierre antes del >, o agregas una etiqueta de cierre separada:
// JSX - debe autocerrar
<img src="photo.jpg" alt="A photo" />
<input type="text" name="username" />
<br />
<hr />
<meta charSet="UTF-8" />
Nota que tambien cambie charset a charSet en ese ultimo ejemplo. Ese es el patron camelCase, que veremos a continuacion.
4. Atributos camelCase
Los atributos HTML no distinguen mayusculas de minusculas. Los nombres de atributos JSX se mapean a propiedades DOM, y las propiedades DOM siguen las convenciones camelCase. Los mas comunes que encontraras:
| HTML | JSX |
|---|---|
onclick | onClick |
onchange | onChange |
tabindex | tabIndex |
maxlength | maxLength |
readonly | readOnly |
autocomplete | autoComplete |
autofocus | autoFocus |
crossorigin | crossOrigin |
charset | charSet |
Los manejadores de eventos son probablemente los que se encuentran con mas frecuencia. Los atributos de eventos HTML usan todo en minusculas:
<!-- HTML -->
<button onclick="handleClick()">Click me</button>
<input onchange="handleChange(event)" />
En JSX, son camelCase y pasas una referencia de funcion (no una cadena):
// JSX
<button onClick={handleClick}>Click me</button>
<input onChange={handleChange} />
Ese ultimo punto: pasar una referencia de funcion en lugar de una llamada de cadena, es una diferencia importante. En HTML onclick, el valor es una cadena de JavaScript para ejecutar. En JSX onClick, el valor es una expresion JavaScript (generalmente una referencia de funcion). Por eso JSX usa llaves {} alrededor del valor en lugar de comillas.
5. Estilos en linea como objetos JavaScript
Esto sorprende a la gente la primera vez. Los estilos en linea HTML son cadenas:
<!-- HTML -->
<div style="color: red; font-size: 16px; margin-top: 8px;">
Styled text
</div>
Los estilos en linea JSX son objetos JavaScript, lo que significa:
- Llaves dobles (un par para la expresion JSX, uno para el literal del objeto)
- Nombres de propiedades en camelCase
- Valores como cadenas o numeros (los numeros para valores de pixeles pueden opcionalmente ser numeros simples)
// JSX
<div style={{ color: 'red', fontSize: '16px', marginTop: '8px' }}>
Styled text
</div>
La regla camelCase tambien se aplica aqui: font-size se convierte en fontSize, margin-top se convierte en marginTop, background-color se convierte en backgroundColor. Cualquier propiedad CSS con guion se convierte en camelCase.
6. Comentarios
En HTML escribes <!-- comment -->. En JSX, los comentarios de estilo HTML no funcionan. Necesitas usar comentarios JavaScript dentro de una expresion JSX:
<!-- HTML comment -->
<div>
<!-- This is a comment -->
<p>Content</p>
</div>
{/* JSX comment */}
<div>
{/* This is a comment */}
<p>Content</p>
</div>
Tambien puedes usar // para comentarios de una sola linea, pero solo dentro de las llaves o en codigo JavaScript normal: no directamente en el marcado JSX.
Trampas menos obvias
Los atributos booleanos se comportan diferente
En HTML, la presencia de un atributo implica true para los atributos booleanos:
<!-- HTML - estos son equivalentes -->
<input disabled>
<input disabled="true">
<input disabled="disabled">
En JSX, los atributos booleanos se pueden escribir en forma abreviada (como HTML), pero la forma explicita se ve diferente:
// JSX - estos son todos equivalentes
<input disabled />
<input disabled={true} />
Lo que debes evitar en JSX es disabled="true" (con comillas), porque eso pasa la cadena "true" en lugar del booleano true. React podria renderizarlo correctamente, pero no es idiomatico y puede causar problemas con algunos componentes.
La prop key en las listas
Cuando conviertes listas HTML a JSX y mapeas datos, React necesita una prop key en cada elemento. Esto no es un problema de conversion HTML a JSX per se: solo aplica cuando generas elementos dinamicamente, pero vale la pena saber antes de obtener la advertencia:
// React advertira sin key
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
Atributos SVG
SVG tiene su propio conjunto de peculiaridades de nombre de atributos. En HTML/SVG, podrias escribir fill-opacity, stroke-width, clip-path. En JSX estos se convierten en fillOpacity, strokeWidth, clipPath. Si pegas SVG en linea de una biblioteca de iconos o una herramienta de diseno, espera hacer algo de limpieza.
tabindex vs tabIndex
Facil de pasar por alto. Si estas construyendo componentes accesibles y estableciendo tabindex manualmente, recuerda que se convierte en tabIndex en JSX. No siempre generara un error si lo omites: React puede aun renderizarlo, pero es una diferencia silenciosa que podria afectar el comportamiento.
Cuando usar un convertidor automatico
Si tienes un gran bloque de HTML: un componente de un sistema de diseno, una plantilla que estas migrando desde una base de codigo no-React, o un fragmento de una fuente en linea, usar un convertidor es la eleccion practica. Reemplazar manualmente cada class con className y cada manejador de eventos con guion es tedioso y propenso a errores.
El convertidor ToolPal HTML to JSX maneja las transformaciones estandar automaticamente: class → className, for → htmlFor, cadenas de estilo → objetos de estilo, elementos void autocerrantes, atributos camelCase. Pegas tu HTML, obtienes de vuelta JSX valido y continuas.
Donde los convertidores automaticos no pueden ayudar es con las decisiones estructurales. Convertir 500 lineas de HTML en una sola instruccion de retorno JSX no te da un componente bien arquitectado: te da un blob convertido. Para cualquier cosa sustancial, el convertidor maneja la sintaxis pero aun necesitas decidir como dividir el resultado en componentes mas pequenos, donde los props deben reemplazar los valores hardcodeados, y que partes deben ser dinamicas.
Usa el convertidor para la transformacion mecanica. Usa tu cerebro para la arquitectura.
Cuando escribir JSX desde cero
Si estas construyendo un nuevo componente y tienes el diseno en tu cabeza (o en un mockup), escribir JSX directamente es a menudo mas rapido que escribir HTML y convertirlo. Escribiras className desde el principio, no olvidaras cerrar tus elementos void, y no terminaras con cadenas de estilo que necesitan conversion.
Los casos donde la conversion realmente ayuda:
Migracion desde un proyecto no-React. Si tienes plantillas HTML existentes: de un sitio HTML/CSS simple, una aplicacion Rails, o una plantilla Jinja, las herramientas de conversion ahorran tiempo real.
Trabajar desde una entrega de diseno. Los disenadores a menudo exportan marcado HTML o proporcionan fragmentos. Pasarlo por un convertidor es mas rapido que volver a escribirlo.
Pegar fragmentos de terceros. Los ejemplos de codigo de documentacion, respuestas de Stack Overflow, o tutoriales a menudo estan escritos en HTML. Una conversion rapida te da JSX utilizable sin edicion manual.
Formularios grandes. Los formularios con decenas de entradas, etiquetas y fieldsets son tediosos de escribir a mano. Pega la estructura HTML, convierte, luego reemplaza los valores hardcodeados con props y estado.
Un ejemplo de conversion realista
Aqui hay un pequeno componente de tarjeta en HTML:
<div class="card" id="product-card">
<img src="/product.jpg" alt="Product photo" class="card-image">
<div class="card-body">
<h2 class="card-title">Product Name</h2>
<p class="card-description" style="color: #666; font-size: 14px;">
A short description of the product.
</p>
<label for="quantity">Quantity</label>
<input type="number" id="quantity" min="1" max="99" readonly>
<button class="btn btn-primary" onclick="addToCart()">Add to Cart</button>
</div>
</div>
Despues de la conversion:
<div className="card" id="product-card">
<img src="/product.jpg" alt="Product photo" className="card-image" />
<div className="card-body">
<h2 className="card-title">Product Name</h2>
<p className="card-description" style={{ color: '#666', fontSize: '14px' }}>
A short description of the product.
</p>
<label htmlFor="quantity">Quantity</label>
<input type="number" id="quantity" min="1" max="99" readOnly />
<button className="btn btn-primary" onClick={addToCart}>Add to Cart</button>
</div>
</div>
Nota cada cambio:
- Todos los
class→className <img>e<input>son autocerrantes con/>- Cadena
style→ objeto de estilo con propiedades camelCase for→htmlForreadonly→readOnlyonclick="addToCart()"→onClick={addToCart}(referencia de funcion, sin parentesis)
Ese ultimo punto sobre el manejador de eventos vale la pena enfatizar. En la version HTML, onclick="addToCart()" es una cadena que se evalua. Si accidentalmente escribes onClick="addToCart()" en JSX, React lanzara un error diciendo que los manejadores de eventos necesitan ser funciones, no cadenas. Y si escribes onClick={addToCart()} (con parentesis), llamaras a la funcion inmediatamente al renderizar en lugar de al hacer clic. Ambos son errores comunes de principiantes.
Algunas cosas que los convertidores no pueden manejar
Contenido dinamico. Un convertidor no sabe que Product Name deberia convertirse en {product.name}. Convertira la estructura pero tendras que reemplazar los valores estaticos con props y estado tu mismo.
Logica del manejador de eventos. onclick="addToCart()" se convertira en onClick={addToCart}, pero addToCart aun necesita estar definido en algun lugar de tu componente. El convertidor te da la sintaxis del atributo; la funcion es tu responsabilidad.
Renderizado condicional. HTML no tiene concepto de "mostrar este elemento solo si X es verdadero". Ese patron es algo que agregas durante el paso de componentizacion, no algo que un convertidor puede inferir del marcado estatico.
Multiples elementos raiz. Si tu fragmento HTML tiene dos elementos hermanos en el nivel superior, el JSX convertido tambien tendra dos elementos hermanos, lo cual es invalido en una instruccion de retorno de componente. Necesitaras envolverlos en un <div> o un fragmento <>...</>.
La conversion de HTML a JSX es una de esas cosas que se siente molesta hasta que se convierte en segunda naturaleza. Despues de unas semanas escribiendo React, escribiras className automaticamente sin pensar. Cerreras reflexivamente tus etiquetas <img />. Pero hasta que se desarrolle esa memoria muscular, herramientas como el convertidor ToolPal HTML to JSX hacen la transicion mas rapida y menos frustrante.
La verdadera habilidad no es memorizar las diferencias de sintaxis: es entender por que existen. Una vez que entiendes que JSX es JavaScript, las reglas dejan de sentirse arbitrarias y empiezan a tener sentido.