Fragmentos de página

Nesta página

O hook page:fragments permite que um plugin contribua com HTML bruto, scripts ou folhas de estilo para páginas públicas. É a ferramenta certa para tags de análise, widgets de terceiros, CSS personalizado e qualquer outra coisa que precise entregar JavaScript ou marcação diretamente no navegador do visitante.

É restrito a plugins nativos porque sua saída é executada como código de primeira parte no navegador, fora de qualquer limite de sandbox. Se você só precisa contribuir com metadados estruturados — tags meta, OpenGraph, JSON-LD, rels <link> na lista de permissões — use page:metadata em vez disso, que está disponível tanto para plugins em sandbox quanto nativos. Veja Hooks: page:metadata.

Capacidade

page:fragments requer a capacidade hooks.page-fragments:register:

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

A capacidade também deve aparecer no descritor.

Onde os fragmentos são renderizados

Os templates optam por receber fragmentos incluindo os componentes relevantes de emdash/ui:

  • <EmDashHead /> — renderiza fragmentos com placement: "head" mais todas as contribuições de page:metadata.
  • <EmDashBodyStart /> — renderiza fragmentos com placement: "body:start".
  • <EmDashBodyEnd /> — renderiza fragmentos com placement: "body:end".

Templates que omitem um desses componentes ignoram silenciosamente fragmentos direcionados a esse posicionamento — seu plugin não quebra, os fragmentos simplesmente não aparecem. Documente seus requisitos de posicionamento no README do plugin.

Tipos de contribuições

Três tipos de contribuições:

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 é "head" | "body:start" | "body:end".

Exemplos

Script externo

Injetar um gerenciador de tags de terceiros:

"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 inline

Executar um pequeno pedaço de JavaScript no topo 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

Adicionar um fallback noscript no 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últiplos fragmentos

Retornar um array para contribuir com múltiplos fragmentos de um ú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

O hook page:fragments recebe a mesma 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 };
	}
}

Use event.page.kind e event.page.pageType para decidir se deve contribuir em uma determinada página — por exemplo, pulando análise em visualizações de administrador ou injetando JSON-LD apenas em posts de blog.

Quando usar page:metadata em vez disso

Se o que você realmente precisa é:

  • Uma meta descrição, diretiva robots ou cartão do Twitter → page:metadata com kind: "meta".
  • Uma propriedade OpenGraph → page:metadata com kind: "property".
  • Um <link> canonical ou alternate → page:metadata com kind: "link".
  • Um grafo JSON-LD → page:metadata com kind: "jsonld".

page:metadata funciona em plugins em sandbox, obtém validação e deduplicação de graça, e evita o ônus de confiança de entregar HTML bruto aos visitantes. Recorra a page:fragments apenas quando você realmente precisar entregar JavaScript ou HTML.