Fragmentos de página

En esta página

El hook page:fragments permite que un plugin contribuya HTML sin procesar, 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 enviar JavaScript o markup directamente al 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 — meta tags, OpenGraph, JSON-LD, <link> rels permitidos — usa page:metadata en su lugar, que está disponible tanto para plugins sandboxed como nativos. Ver Hooks: page:metadata.

Capability

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

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

La capability también debe aparecer en el descriptor.

Dónde se renderizan los fragmentos

Las plantillas optan por recibir fragmentos incluyendo los componentes relevantes de 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 tu requisito de ubicación en el README del plugin.

Tipos de contribución

Un hook puede devolver uno de tres tipos de contribución:

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

El siguiente hook inyecta un administrador de etiquetas de terceros en <head>:

"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

El siguiente hook ejecuta un pequeño fragmento de JavaScript en la parte superior de <body> en páginas de contenido:

"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

El siguiente hook agrega un fallback 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

Un hook puede devolver un array para contribuir múltiples fragmentos a la vez. El siguiente hook agrega tanto un script como un fallback noscript:

"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 determinada — por ejemplo, omitir análisis en vistas previas de administrador o solo inyectar JSON-LD 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 Twitter card → page:metadata con kind: "meta".
  • Una propiedad OpenGraph → page:metadata con kind: "property".
  • Un <link> canónico o alternativo → page:metadata con kind: "link".
  • Un grafo JSON-LD → page:metadata con kind: "jsonld".

page:metadata funciona en plugins sandboxed, obtiene validación y deduplicación gratis, y evita la carga de confianza de enviar HTML sin procesar a los visitantes. Recurre a page:fragments solo cuando necesites enviar JavaScript o HTML.