useSeoMeta
Atajo tipado de useHead para SEO que convierte cada campo (description, Open Graph, Twitter, canonical) en la etiqueta correcta.
Se autoimporta en los componentes y en app/.
import { useSeoMeta } from "@meekojs/meeko/framework";Las etiquetas de SEO tienen reglas que es fácil confundir: description y las de Twitter van en <meta name>, las de Open Graph en <meta property>, la URL canónica es un <link rel="canonical">. useSeoMeta(input) recibe un objeto plano con un campo por etiqueta y produce la correcta para cada uno. Por debajo es useHead: se llama en los mismos sitios, sigue su prioridad y su deduplicación, y acepta un getter o una señal para que las etiquetas sigan a los datos.
Se autoimporta en las aplicaciones.
Uso#
<!-- app/pages/blog/[slug].meeko -->
<script server lang="ts">
const { params } = usePage();
export const post = await posts.find(params.slug);
</script>
<script lang="ts">
useSeoMeta({
title: post.title,
description: post.summary,
ogImage: post.cover,
twitterCard: "summary_large_image",
canonical: `https://example.com/blog/${post.slug}`,
});
</script>
<template><article><h1>{post.title}</h1></article></template>Tipo#
function useSeoMeta(input: HeadSource<SeoMetaInput>): voidinterface SeoMetaInput {
title?: string | null | undefined
description?: string | null | undefined
ogTitle?: string | null | undefined
ogDescription?: string | null | undefined
ogImage?: string | null | undefined
ogImageAlt?: string | null | undefined
ogImageWidth?: string | number | null | undefined
ogImageHeight?: string | number | null | undefined
ogUrl?: string | null | undefined
ogType?: string | null | undefined
ogSiteName?: string | null | undefined
ogLocale?: string | null | undefined
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player' | null | undefined
twitterSite?: string | null | undefined
twitterCreator?: string | null | undefined
twitterTitle?: string | null | undefined
twitterDescription?: string | null | undefined
twitterImage?: string | null | undefined
twitterImageAlt?: string | null | undefined
robots?: string | null | undefined
author?: string | null | undefined
keywords?: string | null | undefined
themeColor?: string | null | undefined
colorScheme?: string | null | undefined
applicationName?: string | null | undefined
referrer?: string | null | undefined
canonical?: string | null | undefined
}Parámetros#
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
input |
SeoMetaInput, ReadonlySignal<SeoMetaInput> o () => SeoMetaInput |
obligatorio | Los campos de SEO. Un campo undefined o null no produce nada. |
| Campo | Etiqueta |
|---|---|
title |
<title> |
description, robots, author, keywords, themeColor, colorScheme, applicationName, referrer |
<meta name="…"> (theme-color, color-scheme, application-name) |
ogTitle, ogDescription, ogImage, ogImageAlt, ogImageWidth, ogImageHeight, ogUrl, ogType, ogSiteName, ogLocale |
<meta property="og:…"> (og:image:alt, og:site_name…) |
twitterCard ("summary", "summary_large_image", "app" o "player"), twitterSite, twitterCreator, twitterTitle, twitterDescription, twitterImage, twitterImageAlt |
<meta name="twitter:…"> |
canonical |
<link rel="canonical"> |
ogImageWidth y ogImageHeight admiten números; el resto, cadenas.
Valor devuelto#
void. Fuera de una aplicación de Meeko no hace nada. Dentro de una, un campo que no está en la lista lanza TypeError: useSeoMeta: unknown field <nombre> al aplicar las etiquetas.
Ejemplos#
Las etiquetas de una ficha de producto#
<script lang="ts">
const props = defineProps<{ data: { name: string; summary: string; image: string; price: number } }>();
useSeoMeta(() => ({
title: props.data.name,
description: props.data.summary,
ogTitle: props.data.name,
ogType: "product",
ogImage: props.data.image,
ogImageAlt: `Foto de ${props.data.name}`,
ogImageWidth: 1200,
ogImageHeight: 630,
}));
</script>Con un getter, las etiquetas cambian al navegar a otro producto aunque el componente se conserve.
Una página que no se indexa#
import { useSeoMeta } from "@meekojs/meeko/framework";
// En el <script> de app/pages/account/settings.meeko
useSeoMeta({ robots: "noindex, nofollow" });Canónica y Open Graph automáticos#
Para no repetir canonical, ogUrl, ogTitle y ogDescription en cada página, document.seo las deriva de la URL, el título y la descripción finales:
// meeko.config.ts
import { defineMeekoConfig } from "@meekojs/meeko/framework";
export default defineMeekoConfig({
site: "https://example.com",
document: { seo: true, head: { titleTemplate: "%s · Mi sitio" } },
});Una página que declara la misma etiqueta con useSeoMeta sustituye a la derivada.
Errores comunes#
Un campo mal escrito#
useSeoMeta({ title: "Precios", ogimage: "/og/pricing.png" });meeko check da TS2353: Object literal may only specify known properties, and 'ogimage' does not exist in type 'HeadSource<SeoMetaInput>' y, en ejecución, TypeError: useSeoMeta: unknown field ogimage.
useSeoMeta({ title: "Precios", ogImage: "/og/pricing.png" });Un twitterCard que no existe#
useSeoMeta({ twitterCard: "large" });meeko check da TS2322: Type '"large"' is not assignable to type '"app" | "player" | "summary" | "summary_large_image" | null | undefined'.
useSeoMeta({ twitterCard: "summary_large_image" });Un objeto con datos que cambian#
useSeoMeta({ title: props.data.name });El título se fija con el valor de ese momento: si el layout o la página se conservan al navegar y la prop cambia, la etiqueta se queda con el producto anterior. Pasa un getter:
useSeoMeta(() => ({ title: props.data.name }));Una URL relativa en ogImage o canonical#
useSeoMeta({ ogImage: "/og/home.png", canonical: "/" });No hay error, pero los buscadores y las redes sociales esperan URL absolutas en og:image y en la canónica. Usa el origen público del sitio, o document.seo para la canónica:
useSeoMeta({ ogImage: "https://example.com/og/home.png", canonical: "https://example.com/" });Notas#
- SSR. Las etiquetas salen en el HTML del servidor, que es lo que leen los buscadores y las vistas previas de las redes sociales. Calcúlalas con datos que el SSR ya tiene: los de
<script server>,load, las props o unawait useApi. - Prioridad. La misma que
useHead: elheadde la ruta gana a la página, la página a los layouts y éstos adocument. Dentro de un nivel, gana la llamada posterior. descriptionde la página. Si elheadde la ruta (export const headoroute.head) ya declaradescription, ésta gana sobre la deuseSeoMetadel componente.- Escape. Los valores van en atributos y Meeko los escapa: un título con comillas o
<es seguro.