Metadatos del Documento - Renderiza etiquetas title, meta y link en cualquier parte del árbol de componentes
Receta
function BlogPost({ post }: { post: { title: string; description: string; author: string } }) {
return (
<article>
{/* Estas se elevan a <head> automáticamente */}
<title>{post.title}</title>
<meta name="description" content={post.description} />
<meta name="author" content={post.author} />
<link rel="canonical" href={`https://example.com/blog/${post.title}`} />
<h1>{post.title}</h1>
<p>{post.description}</p>
</article>
);
}Cuándo usarlo: Usa metadatos integrados siempre que un componente necesite establecer el título de la página, descripción u otras etiquetas head. Esto elimina la necesidad de react-helmet o efectos manuales de document.title.
Ejemplo Funcional
// Una aplicación de varias páginas donde cada ruta establece sus propios metadatos
function ProductPage({ product }: {
product: {
name: string;
description: string;
price: number;
image: string;
category: string;
};
}) {
return (
<main>
{/* Título de la página */}
<title>{product.name} | My Store</title>
{/* Etiquetas meta SEO */}
<meta name="description" content={product.description} />
<meta name="keywords" content={`${product.category}, buy, shop`} />
{/* Etiquetas Open Graph */}
<meta property="og:title" content={product.name} />
<meta property="og:description" content={product.description} />
<meta property="og:image" content={product.image} />
<meta property="og:type" content="product" />
{/* Tarjeta de Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={product.name} />
<meta name="twitter:image" content={product.image} />
{/* Precarga la imagen del producto */}
<link rel="preload" href={product.image} as="image" />
{/* Hoja de estilos para esta página */}
<link rel="stylesheet" href="/styles/product.css" precedence="default" />
{/* Contenido de la página */}
<div className="max-w-4xl mx-auto p-6">
<img src={product.image} alt={product.name} />
<h1 className="text-3xl font-bold">{product.name}</h1>
<p className="text-xl">${product.price.toFixed(2)}</p>
<p>{product.description}</p>
</div>
</main>
);
}
function SettingsPage() {
return (
<div>
<title>Settings | My Store</title>
<meta name="robots" content="noindex" />
<h1>Account Settings</h1>
{/* ... */}
</div>
);
}
export { ProductPage, SettingsPage };Lo que esto demuestra:
<title>renderizado en línea, automáticamente elevado a<head>- Etiquetas meta SEO (descripción, palabras clave) establecidas por página
- Etiquetas Open Graph y Twitter Card para compartir en redes sociales
<link rel="preload">para optimización del rendimiento<link rel="stylesheet">conprecedencepara ordenar CSS- Diferentes páginas estableciendo metadatos completamente diferentes
Análisis Profundo
Cómo Funciona
- React 19 reconoce elementos
<title>,<meta>y<link>cuando se renderizan dentro de un componente y los eleva al<head>del documento automáticamente. - Esto funciona en todos los modos de renderizado: SPAs solo del cliente, SSR de streaming y Server Components.
- Durante SSR, las etiquetas de metadatos se incluyen en el
<head>HTML inicial. Durante la navegación del lado del cliente, React actualiza el<head>agregando, eliminando o actualizando los elementos elevados. - Las etiquetas
<title>se deduplicanplicamos -- solo gana la última<title>renderizada si varios componentes renderizan una. - Las etiquetas
<meta>se desduplican por el atributonameoproperty. Dos etiquetas<meta name="description">de diferentes componentes resultarán en solo la más reciente. - Las etiquetas
<link rel="stylesheet">soportan un atributoprecedenceque controla el orden de inserción. Las hojas de estilo con el mismoprecedencese agrupan juntas. React también deduplica enlaces de hojas de estilo porhref. - Otros tipos de
<link>(preload, icon, canonical, etc.) se elevan pero no se deduplicanplican por defecto.
Variaciones
Título dinámico con datos en vivo:
function ChatRoom({ roomName, unreadCount }: { roomName: string; unreadCount: number }) {
const titlePrefix = unreadCount > 0 ? `(${unreadCount}) ` : "";
return (
<div>
<title>{titlePrefix}{roomName} | Chat</title>
<h1>{roomName}</h1>
{/* chat UI */}
</div>
);
}Componentes anidados agregando metadatos:
function Layout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{/* Metadatos base */}
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
{children}
</body>
</html>
);
}
function AboutPage() {
return (
<Layout>
{/* Los metadatos específicos de la página se fusionan con los metadatos del layout */}
<title>About Us</title>
<meta name="description" content="Learn about our company" />
<h1>About Us</h1>
</Layout>
);
}Orden de precedencia de hojas de estilo:
function App() {
return (
<>
{/* precedencia "default" carga primero */}
<link rel="stylesheet" href="/base.css" precedence="default" />
{/* precedencia "high" carga después de "default" */}
<link rel="stylesheet" href="/theme.css" precedence="high" />
</>
);
}
function Widget() {
return (
<>
{/* Misma precedencia se agrupa con otras hojas de estilo "default" */}
<link rel="stylesheet" href="/widget.css" precedence="default" />
<div>Widget content</div>
</>
);
}Notas de TypeScript
- Los elementos JSX
<title>,<meta>y<link>usan sus tipos de atributos HTML estándar deReact.JSX.IntrinsicElements. - El atributo
precedenceen<link>es una extensión específica de React tipada comostring | undefined. - No se necesitan importaciones especiales -- estos son elementos JSX estándar.
Trampas
- Múltiples etiquetas
<title>-- Solo se aplica la última<title>renderizada. Si dos componentes hermanos renderizan ambos<title>, el resultado depende del orden de renderizado. Solución: Establece<title>en solo un componente por ruta, típicamente el componente a nivel de página. - Las APIs de metadatos del framework pueden entrar en conflicto -- Next.js tiene su propia exportación
metadataygenerateMetadata. Usar ambas eso y etiquetas<title>en línea puede causar duplicados. Solución: Elige un enfoque por proyecto. Para aplicaciones Next.js, prefiere el APImetadatadel framework. - Etiquetas meta fuera de
<head>en HTML SSR -- React eleva las etiquetas durante el renderizado, pero si inspeccionas la respuesta HTML sin procesar, aparecen en<head>. Si ocurren desincronizaciones de hidratación, comprueba las diferencias servidor/cliente. Solución: Asegúrate de que los valores de metadatos sean deterministas (sinDate.now()oMath.random()). <link rel="stylesheet">sinprecedence-- Si omitesprecedence, el enlace de la hoja de estilo se trata como un enlace regular y no se deduplica ni ordena por React. Solución: Siempre agregaprecedencea los enlaces de hojas de estilo para la ordenación administrada por React.- Sin soporte para elevación de
<script>-- React 19 no eleva las etiquetas<script>al<head>. Solución: Usapreinitdereact-dompara scripts, o coloca etiquetas script en tu plantilla HTML.
Alternativas
| Enfoque | Cuándo elegir |
|---|---|
| Metadatos integrados (React 19) | Opción predeterminada para nuevos proyectos React 19 |
Exportación metadata de Next.js | Proyectos Next.js App Router con metadatos estáticos o dinámicos |
react-helmet / react-helmet-async | Proyectos React 18 o anteriores |
document.title manual en useEffect | Cambios de título rápidos solo del cliente |
<head> HTML en plantilla | Metadatos estáticos que nunca cambian |
Preguntas Frecuentes
¿Cómo maneja React 19 las etiquetas title, meta y link renderizadas dentro de componentes?
- React reconoce estos elementos y los eleva al
<head>del documento automáticamente - Esto funciona en todos los modos de renderizado: SPAs solo del cliente, SSR de streaming y Server Components
- Durante la navegación del lado del cliente, React actualiza el
<head>agregando, eliminando o actualizando elementos elevados
¿Qué sucede si múltiples componentes renderizan una etiqueta title?
- Solo se aplica la última
<title>renderizada -- React las deduplica - Si dos componentes hermanos renderizan ambos
<title>, el resultado depende del orden de renderizado - Establece
<title>en solo un componente por ruta, típicamente el componente a nivel de página
¿Cómo se deduplicanplicanplicanplican las etiquetas meta?
- Las etiquetas
<meta>se desduplican por el atributonameoproperty - Dos etiquetas
<meta name="description">de diferentes componentes resultan en solo la más reciente - Otros tipos de
<link>(preload, icon, canonical) se elevan pero no se deduplicanplicanplicanplican por defecto
¿Qué hace el atributo precedence en las etiquetas de enlace de hoja de estilo?
precedencecontrola el orden de inserción de hojas de estilo en el<head>- Las hojas de estilo con el mismo valor
precedencese agrupan juntas - React también deduplica enlaces de hojas de estilo por
hrefy suspende el renderizado hasta que la hoja de estilo carga
¿Cómo estableces etiquetas Open Graph y Twitter Card con los metadatos de React 19?
function Page({ title, description, image }) {
return (
<>
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={image} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
{/* page content */}
</>
);
}¿Puedes actualizar dinámicamente el título con datos en vivo (p. ej., conteo no leído)?
function ChatRoom({ roomName, unreadCount }) {
const prefix = unreadCount > 0 ? `(${unreadCount}) ` : "";
return (
<>
<title>{prefix}{roomName} | Chat</title>
{/* chat UI */}
</>
);
}El título se actualiza reactivamente mientras unreadCount cambia.
¿Pueden componentes anidados de layouts y páginas ambos contribuir metadatos?
- Sí. Un layout puede establecer metadatos base (
<meta charSet>,<link rel="icon">) y las páginas secundarias agregan etiquetas específicas de página - Las etiquetas se fusionan -- la
<title>específica de página anula la del layout si ambas renderizan una - La deduplicación de
<meta>porname/propertyasegura sin duplicados
Trampa: ¿Debo usar metadatos en línea de React 19 o exportación metadata de Next.js?
- Next.js tiene su propia exportación
metadataygenerateMetadataque puede entrar en conflicto con etiquetas en línea - Usar ambas puede causar etiquetas duplicadas en
<head> - Elige un enfoque por proyecto; para aplicaciones Next.js, prefiere el API
metadatadel framework
Trampa: ¿Eleva React 19 las etiquetas script al head?
- No. React 19 no eleva las etiquetas
<script>al<head> - Usa
preinitdereact-dompara scripts, o coloca etiquetas script en tu plantilla HTML - Esto solo aplica a elementos
<title>,<meta>y<link>
¿Qué sucede si una etiqueta de enlace de hoja de estilo no tiene el atributo precedence?
- Sin
precedence, la hoja de estilo se trata como un enlace regular y no se deduplica ni ordena por React - React tampoco suspenderá el renderizado para ella, potencialmente causando un destello de contenido sin estilo
- Siempre agrega
precedencea los enlaces de hoja de estilo que deseas que React administre
¿Cómo se tipifica el atributo precedence en TypeScript?
- El atributo
precedenceen<link>es una extensión específica de React tipada comostring | undefined <title>,<meta>y<link>usan sus tipos de atributos HTML estándar deReact.JSX.IntrinsicElements- No se necesitan importaciones especiales -- estos son elementos JSX estándar
¿Cómo aseguras que los valores de metadatos no causen desincronizaciones de hidratación?
- Asegúrate de que los valores de metadatos sean deterministas -- evita
Date.now(),Math.random()u otros valores no deterministas - El servidor y cliente deben producir el mismo contenido de metadatos durante la hidratación
- Usa valores estables de props, params o consultas de base de datos
Relacionado
- Asset Loading -- Precarga de recursos con
preloadypreinit - Server Components -- Metadatos en páginas renderizadas en servidor
- Overview -- Lista completa de características de React 19