Fragmentos de página

En esta página

El hook page:fragments permite que un plugin contribuya HTML crudo, scripts u hojas de estilo a páginas públicas. Es la herramienta adecuada para etiquetas de análisis, widgets de terceros, CSS personalizado y cualquier otra cosa que necesite entregar JavaScript o marcado directamente en el navegador del visitante.

Está restringido a plugins nativos porque su salida se ejecuta como código de primera parte en el navegador, fuera de cualquier límite de sandbox. Si solo necesitas contribuir metadatos estructurados — etiquetas meta, OpenGraph, JSON-LD, rels <link> en la lista blanca — usa page:metadata en su lugar, que está disponible tanto para plugins en sandbox como nativos. Consulta Hooks: page:metadata.

Capacidad

page:fragments requiere la capacidad hooks.page-fragments:register:

return definePlugin({
	id: "analytics-gtm",
	version: "1.0.0",
	capabilities: ["hooks.page-fragments:register"],
	// ...
});

La capacidad también debe aparecer en el descriptor.

Dónde se renderizan los fragmentos

Las plantillas optan por recibir fragmentos incluyendo los componentes relevantes desde emdash/ui:

  • <EmDashHead /> — renderiza fragmentos con placement: "head" más todas las contribuciones de page:metadata.
  • <EmDashBodyStart /> — renderiza fragmentos con placement: "body:start".
  • <EmDashBodyEnd /> — renderiza fragmentos con placement: "body:end".

Las plantillas que omiten uno de estos componentes ignoran silenciosamente los fragmentos dirigidos a esa ubicación — tu plugin no se rompe, los fragmentos simplemente no aparecen. Documenta tus requisitos de ubicación en el README del plugin.

Tipos de contribuciones

Tres tipos de contribuciones:

type PageFragmentContribution =
	| {
			kind: "external-script";
			placement: PagePlacement;
			src: string;
			async?: boolean;
			defer?: boolean;
			attributes?: Record<string, string>;
			key?: string;
	  }
	| {
			kind: "inline-script";
			placement: PagePlacement;
			code: string;
			attributes?: Record<string, string>;
			key?: string;
	  }
	| {
			kind: "html";
			placement: PagePlacement;
			html: string;
			key?: string;
	  };

PagePlacement es "head" | "body:start" | "body:end".

Ejemplos

Script externo

Inyectar un administrador de etiquetas de terceros:

"page:fragments": async (event, ctx) => {
	const containerId = await ctx.kv.get<string>("settings:gtmContainerId");
	if (!containerId) return null;

	return {
		kind: "external-script",
		placement: "head",
		src: `https://www.googletagmanager.com/gtm.js?id=${containerId}`,
		async: true,
	};
},

Script en línea

Ejecutar un pequeño fragmento de JavaScript en la parte superior de <body>:

"page:fragments": async (event, ctx) => {
	if (event.page.kind !== "content") return null;
	return {
		kind: "inline-script",
		placement: "body:start",
		code: `window.contentId = ${JSON.stringify(event.page.content?.id)};`,
	};
},

Fragmento HTML

Agregar un respaldo noscript al final de <body>:

"page:fragments": async (event, ctx) => {
	const containerId = await ctx.kv.get<string>("settings:gtmContainerId");
	if (!containerId) return null;

	return {
		kind: "html",
		placement: "body:end",
		html: `<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${containerId}" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>`,
	};
},

Múltiples fragmentos

Devolver un array para contribuir múltiples fragmentos desde un único hook:

"page:fragments": async (event, ctx) => {
	const id = await ctx.kv.get<string>("settings:gtmContainerId");
	if (!id) return null;

	return [
		{
			kind: "external-script",
			placement: "head",
			src: `https://www.googletagmanager.com/gtm.js?id=${id}`,
			async: true,
		},
		{
			kind: "html",
			placement: "body:end",
			html: `<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${id}" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>`,
		},
	];
},

Evento de página

El hook page:fragments recibe la misma forma de evento que page:metadata:

{
	page: {
		url: string;
		path: string;
		locale: string | null;
		kind: "content" | "custom";
		pageType: string;
		title: string | null;
		pageTitle?: string | null;
		description: string | null;
		canonical: string | null;
		image: string | null;
		content?: { collection: string; id: string; slug: string | null };
	}
}

Usa event.page.kind y event.page.pageType para decidir si contribuir en una página dada — por ejemplo, omitir análisis en vistas previas de administrador o inyectar JSON-LD solo en publicaciones de blog.

Cuándo usar page:metadata en su lugar

Si lo que realmente necesitas es:

  • Una meta descripción, directiva robots o tarjeta de Twitter → page:metadata con kind: "meta".
  • Una propiedad OpenGraph → page:metadata con kind: "property".
  • Un <link> canonical o alternate → page:metadata con kind: "link".
  • Un grafo JSON-LD → page:metadata con kind: "jsonld".

page:metadata funciona en plugins en sandbox, obtiene validación y deduplicación de forma gratuita, y evita la carga de confianza de entregar HTML crudo a los visitantes. Recurre a page:fragments solo cuando realmente necesites entregar JavaScript o HTML.