En esta clase vamos a solucionar un problema común al trabajar con la etiqueta <title> en React: la duplicación de títulos en el DOM y cómo gestionar correctamente el título de la página de forma dinámica.

El problema con useEffect

Es habitual ver código que actualiza el título de la página usando useEffect:

useEffect(() => {
  document.title = 'Mi aplicación React'
}, [])

Si bien esto funciona, no es la forma más limpia de hacerlo en React. Existe una alternativa más elegante y declarativa.

La solución: usar <title> directamente

React permite usar etiquetas de SEO como <title> o <meta> directamente dentro del JSX, sin necesidad de manipular el DOM manualmente.

En lugar de usar useEffect, puedes simplemente renderizar la etiqueta:

function App() {
  return (
    <>
      <title>Mi aplicación React</title>
      <div>{/* Resto de tu aplicación */}</div>
    </>
  )
}

Ventajas de este enfoque

  • Más declarativo - El título es parte del render
  • Menos código - No necesitas un hook adicional
  • Más React - Todo está en el JSX
  • Actualización automática - Se actualiza con cada render

El problema oculto: duplicación del título

Sin embargo, al implementar esta solución, aparece un problema inesperado. Si inspeccionas el DOM en las DevTools del navegador, verás que aparecen dos etiquetas <title> en el <head>.

¿Por qué ocurre esto?

Esto sucede porque:

  1. React está renderizando su propio <title> desde el componente
  2. Pero en el archivo index.html ya existe otro <title> por defecto

Ambos títulos coexisten en el DOM, lo cual no es correcto y puede causar problemas de SEO.

⚠️ Importante: Según la documentación oficial de React, solo debes renderizar un único <title> a la vez. Si más de un componente renderiza una etiqueta <title> al mismo tiempo, React colocará todos esos títulos en el document head. Cuando esto sucede, el comportamiento de los navegadores y motores de búsqueda no está definido.

Solución: eliminar el título del index.html

La solución es simple: elimina la etiqueta <title> del archivo index.html y deja que React controle el título completamente desde tus componentes.

Antes (index.html)

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React</title>
    <!-- ❌ Eliminar esta línea -->
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

Después (index.html)

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- ✅ Sin título aquí, React lo gestiona -->
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

De esta forma, evitas duplicados y el único título en el DOM es el que renderiza React.

Actualizar el título dinámicamente

Una de las grandes ventajas de gestionar el título desde React es que puede depender del estado de la aplicación.

Puedes mostrar diferentes títulos según lo que esté pasando:

function App() {
  const [jobs, setJobs] = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  // Lógica de carga de datos...

  return (
    <>
      <title>
        {loading ? 'Cargando...' : error ? 'Error al cargar' : `${jobs.length} empleos encontrados`}
      </title>
      <div>{/* Resto de la aplicación */}</div>
    </>
  )
}

💡 Nota sobre interpolación: Los hijos del componente <title> deben ser una cadena única de texto. Por eso es importante usar template literals con interpolación como `${jobs.length} empleos encontrados`, ya que esto genera una única cadena. Si usaras algo como {jobs.length} empleos encontrados sin las comillas invertidas, estarías pasando un array de dos elementos (el número y el texto), lo que causaría un error.

Casos de uso del título dinámico

Con este enfoque, el título de la pestaña del navegador se actualiza en tiempo real mostrando:

  • “Cargando…” mientras se obtienen los datos
  • “Error al cargar” si ocurre un problema
  • “25 empleos encontrados” cuando los datos llegan correctamente
  • “No se encontraron empleos” si la búsqueda no devuelve resultados

Todo esto sin tocar document.title manualmente ni usar useEffect.

Ventajas del título dinámico

  1. Feedback instantáneo - El usuario ve el progreso en la pestaña
  2. Mejor UX - Incluso si no está mirando la página activamente
  3. Más profesional - Las aplicaciones modernas funcionan así
  4. SEO amigable - Los crawlers ven el título correcto

Ejemplo completo

Aquí tienes un ejemplo completo integrando todo:

function JobsApp() {
  const [jobs, setJobs] = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    fetch('https://jscamp-api.vercel.app/api/jobs')
      .then((res) => res.json())
      .then((data) => {
        setJobs(data)
        setLoading(false)
      })
      .catch((err) => {
        setError(err)
        setLoading(false)
      })
  }, [])

  // El título se actualiza automáticamente con cada cambio de estado
  const getTitle = () => {
    if (loading) return 'Cargando empleos...'
    if (error) return 'Error al cargar empleos'
    if (jobs.length === 0) return 'No se encontraron empleos'
    return `${jobs.length} empleos disponibles - JSCamp`
  }

  return (
    <>
      <title>{getTitle()}</title>
      <main>
        {loading && <p>Cargando...</p>}
        {error && <p>Error: {error.message}</p>}
        {!loading && !error && <JobsList jobs={jobs} />}
      </main>
    </>
  )
}

Casos especiales donde <title> no va al <head>

Según la documentación oficial de React, hay dos excepciones donde <title> NO será colocado en el <head> del documento:

1. Dentro de un componente <svg>

Si <title> está dentro de un <svg>, no tiene un comportamiento especial, porque en este contexto no representa el título del documento, sino que es una anotación de accesibilidad para ese gráfico SVG:

<svg>
  <title>Gráfico de ventas</title>
  {/* Contenido del SVG */}
</svg>

2. Con la prop itemProp

Si <title> tiene una prop itemProp, tampoco tiene un comportamiento especial, porque en este caso no representa el título del documento, sino metadatos sobre una parte específica de la página (schema.org):

<div itemScope itemType="https://schema.org/Movie">
  <title itemProp="name">El Padrino</title>
  {/* Más metadatos estructurados */}
</div>

Resumen

En esta clase has aprendido:

  1. ✅ Por qué usar <title> directamente en JSX es mejor que useEffect
  2. ✅ El problema de tener títulos duplicados en el DOM
  3. ✅ Cómo solucionarlo eliminando el <title> del index.html
  4. ✅ Cómo actualizar el título dinámicamente según el estado
  5. ✅ La importancia de usar cadenas únicas de texto en el título
  6. ✅ Casos especiales donde <title> no va al <head>
  7. ✅ Casos de uso prácticos: cargando, errores, resultados

Recuerda: En React, controla el <head> desde tus componentes, no desde el HTML estático. Esto te da más flexibilidad y un código más mantenible. Y siempre asegúrate de renderizar solo un <title> a la vez para evitar comportamientos indefinidos.

En aplicaciones más grandes, podrías usar librerías como react-helmet o el componente <Head> de frameworks como Next.js, pero el principio es el mismo: gestiona el SEO de forma declarativa desde React.