From 9e0d8fe1cbb03d4f76e5a5c2a3391e157c5d4ed8 Mon Sep 17 00:00:00 2001 From: Andrei Ivascu <7030530+aivascu@users.noreply.github.com> Date: Tue, 1 Sep 2026 13:28:28 +0300 Subject: [PATCH] Fix link previews in Teams and Telegram by prioritizing OG meta tags. Crawlers often read only the first ~16 KB of HTML. Nuxt inlined ~19 KB of CSS before Open Graph tags, so IM apps missed them while full-page parsers still worked. Render SEO tags immediately after charset/viewport. --- site/app/composables/usePageSeo.ts | 29 ++++++++++++++++++----------- site/nuxt.config.ts | 25 ++++++++++++++----------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/site/app/composables/usePageSeo.ts b/site/app/composables/usePageSeo.ts index 511f807..5ed6e53 100644 --- a/site/app/composables/usePageSeo.ts +++ b/site/app/composables/usePageSeo.ts @@ -2,6 +2,11 @@ const DEFAULT_DESCRIPTION = 'AutoFixture makes unit tests more productive by creating anonymous test data for .NET.' const DEFAULT_OG_IMAGE = '/og-image.png' +const OG_IMAGE_WIDTH = '1200' +const OG_IMAGE_HEIGHT = '630' + +/** Crawlers such as Telegram and Teams often read only the first ~16 KB of HTML. */ +const seoTagPriority = -5 type PageSeoOptions = { title?: MaybeRefOrGetter @@ -21,17 +26,19 @@ export function usePageSeo(options: PageSeoOptions = {}) { useHead({ title, meta: computed(() => [ - { name: 'description', content: description.value }, - { property: 'og:title', content: title.value }, - { property: 'og:description', content: description.value }, - { property: 'og:image', content: image.value }, - { property: 'og:url', content: url.value }, - { property: 'og:type', content: 'website' }, - { property: 'og:site_name', content: 'AutoFixture' }, - { name: 'twitter:card', content: 'summary_large_image' }, - { name: 'twitter:title', content: title.value }, - { name: 'twitter:description', content: description.value }, - { name: 'twitter:image', content: image.value }, + { name: 'description', content: description.value, tagPriority: seoTagPriority }, + { property: 'og:site_name', content: 'AutoFixture', tagPriority: seoTagPriority }, + { property: 'og:type', content: 'website', tagPriority: seoTagPriority }, + { property: 'og:title', content: title.value, tagPriority: seoTagPriority }, + { property: 'og:description', content: description.value, tagPriority: seoTagPriority }, + { property: 'og:image', content: image.value, tagPriority: seoTagPriority }, + { property: 'og:image:width', content: OG_IMAGE_WIDTH, tagPriority: seoTagPriority }, + { property: 'og:image:height', content: OG_IMAGE_HEIGHT, tagPriority: seoTagPriority }, + { property: 'og:url', content: url.value, tagPriority: seoTagPriority }, + { name: 'twitter:card', content: 'summary_large_image', tagPriority: seoTagPriority }, + { name: 'twitter:title', content: title.value, tagPriority: seoTagPriority }, + { name: 'twitter:description', content: description.value, tagPriority: seoTagPriority }, + { name: 'twitter:image', content: image.value, tagPriority: seoTagPriority }, ]), }) } diff --git a/site/nuxt.config.ts b/site/nuxt.config.ts index e47ab13..5af740e 100644 --- a/site/nuxt.config.ts +++ b/site/nuxt.config.ts @@ -24,6 +24,7 @@ const siteUrl = 'https://autofixture.com' const siteDescription = 'AutoFixture makes unit tests more productive by creating anonymous test data for .NET.' const ogImage = `${siteUrl}/og-image.png` +const seoTagPriority = -5 export default defineNuxtConfig({ runtimeConfig: { @@ -47,17 +48,19 @@ export default defineNuxtConfig({ head: { title: 'AutoFixture', meta: [ - { name: 'description', content: siteDescription }, - { property: 'og:site_name', content: 'AutoFixture' }, - { property: 'og:type', content: 'website' }, - { property: 'og:title', content: 'AutoFixture' }, - { property: 'og:description', content: siteDescription }, - { property: 'og:image', content: ogImage }, - { property: 'og:url', content: `${siteUrl}/` }, - { name: 'twitter:card', content: 'summary_large_image' }, - { name: 'twitter:title', content: 'AutoFixture' }, - { name: 'twitter:description', content: siteDescription }, - { name: 'twitter:image', content: ogImage }, + { name: 'description', content: siteDescription, tagPriority: seoTagPriority }, + { property: 'og:site_name', content: 'AutoFixture', tagPriority: seoTagPriority }, + { property: 'og:type', content: 'website', tagPriority: seoTagPriority }, + { property: 'og:title', content: 'AutoFixture', tagPriority: seoTagPriority }, + { property: 'og:description', content: siteDescription, tagPriority: seoTagPriority }, + { property: 'og:image', content: ogImage, tagPriority: seoTagPriority }, + { property: 'og:image:width', content: '1200', tagPriority: seoTagPriority }, + { property: 'og:image:height', content: '630', tagPriority: seoTagPriority }, + { property: 'og:url', content: `${siteUrl}/`, tagPriority: seoTagPriority }, + { name: 'twitter:card', content: 'summary_large_image', tagPriority: seoTagPriority }, + { name: 'twitter:title', content: 'AutoFixture', tagPriority: seoTagPriority }, + { name: 'twitter:description', content: siteDescription, tagPriority: seoTagPriority }, + { name: 'twitter:image', content: ogImage, tagPriority: seoTagPriority }, ], link: [ { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },