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 conplacement: "head"más todas las contribuciones depage:metadata.<EmDashBodyStart />— renderiza fragmentos conplacement: "body:start".<EmDashBodyEnd />— renderiza fragmentos conplacement: "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:metadataconkind: "meta". - Una propiedad OpenGraph →
page:metadataconkind: "property". - Un
<link>canónico o alternativo →page:metadataconkind: "link". - Un grafo JSON-LD →
page:metadataconkind: "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.