Open Graph tags in SvelteKit
svelte:head is rendered on the server and inserted where %sveltekit.head% sits in app.html. Return the title, description, and image from load, and read them once in the root layout. Deploy it, then paste the public URL into the checker above.
Add the tags
- svelte:head, fed by page.data. Svelte's docs say svelte:head inserts elements into document.head, and during server rendering that head content is separate from the body. The element has to be at the top level of the component. It cannot sit inside an if block or another element. SvelteKit's load docs say a parent layout's own data prop includes that layout and its parents, and a child page's return value is read in the layout as page.data. Their example is a title in svelte:head in the root layout, taken from page.data.title. Import page from $app/state. On a SvelteKit 2 release from before that module, import page from $app/stores and read $page.data. og: tags use property. twitter:card uses name. summary_large_image is the large card on X. The How to Add Open Graph Tags page is those properties. The Next.js Open Graph page is the same fields as a metadata export.
<script> import { page } from "$app/state"; let { children } = $props(); </script> <svelte:head> <title>{page.data.title}</title> <meta name="description" content={page.data.description} /> <meta property="og:title" content={page.data.title} /> <meta property="og:description" content={page.data.description} /> <meta property="og:url" content={page.data.canonical} /> <meta property="og:type" content="article" /> <meta property="og:image" content={page.data.image} /> <meta property="og:image:width" content="1200" /> <meta property="og:image:height" content="630" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:image" content={page.data.image} /> </svelte:head> {@render children()} - One set of tags, and the prerender origin. Return the card fields from load. When a layout load and a page load use the same key, SvelteKit's load docs say the last one wins, so a root layout can return a default title and image and the page load can replace them. The layout then has a single svelte:head. A second svelte:head on the page is added as well. SvelteKit does not drop the layout's og:title when the page prints another, and this checker reads the first og:title, og:description, and og:image. A Svelte 4 layout uses a slot element in place of {@render children()}, and it imports page from $app/stores. During a real request, url.origin is the host that was requested. During prerender, the default origin is http://sveltekit-prerender until kit.prerender.origin is the public https origin. An og:image built from that default is an address no crawler can fetch. Set the origin before you trust absolute URLs in a prerendered file.
import type { LayoutServerLoad } from "./$types"; export const load: LayoutServerLoad = ({ url }) => { return { title: "Example", description: "One or two sentences.", canonical: new URL(url.pathname, url.origin).href, image: new URL("/og.png", url.origin).href, }; }; - %sveltekit.head% and the adapter. src/app.html is the document shell. The starter places %sveltekit.head% inside head, and that token is where svelte:head is written during server rendering. Removing the token, or placing it outside head, leaves the component's tags out of the HTML. A title or og:image written in app.html above that token is on every page. It comes first, so it is the value this checker keeps when the layout also prints one. @sveltejs/adapter-auto, adapter-node, adapter-vercel, adapter-cloudflare, and adapter-static decide how that HTML is served or written to disk. They do not generate Open Graph tags. adapter-static prerenders by saving the server-rendered response. The page-options docs say ssr set to false renders an empty shell, and prerendering saves that shell. A shell has no svelte:head from the page. Leave ssr on for any route whose card matters. The adapter line in svelte.config.js is the deployment target. The tags are the svelte:head above it.
import adapter from "@sveltejs/adapter-auto"; /** @type {import("@sveltejs/kit").Config} */ const config = { kit: { adapter: adapter(), prerender: { origin: "https://example.com", }, }, }; export default config; - Check the public URL. Paste the public URL. Peekly reads the HTML your server sends, or the file the prerender wrote, and grades og:title, og:description, og:image, and twitter:card. It downloads the image. An image host of sveltekit-prerender is the default prerender origin. A missing tag means the head was not in the response: ssr is off, the %sveltekit.head% token was removed, or the values were assigned in onMount. The Open Graph Checker is that grade. The Twitter Card Validator is the card. The Astro Open Graph, Nuxt Open Graph, and Remix Open Graph pages are the same tags in those frameworks. The Open Graph Image Size page is the 1200×630 file the width and height should describe.
Tags covered: og:title · og:description · og:image · og:url · og:type · twitter:card · twitter:image
More checkers
Twitter Card ValidatorOpen Graph CheckerLinkedIn Link Preview CheckerDiscord Embed PreviewWhatsApp Link Preview CheckerSlack Link Preview CheckeriMessage Link Preview CheckerFacebook Link Preview CheckerOpen Graph Image SizeTelegram Link Preview CheckerReddit Link Preview CheckerNext.js Open GraphRefresh a Link Preview CacheBluesky Link Preview CheckerMastodon Link Preview CheckerMicrosoft Teams Link Preview CheckerFix a Broken Open Graph PreviewInstagram Link Preview CheckerPinterest Link Preview CheckerTikTok Link Preview CheckerYouTube Link Preview CheckerThreads Link Preview CheckerSnapchat Link Preview CheckerHow to Add Open Graph TagsOpen Graph Image Not UpdatingDiscord Link PreviewX (Twitter) Link PreviewWordPress Open GraphShopify Open GraphAstro Open GraphNuxt Open GraphRemix Open GraphAll platforms
What each platform reads
- X / Twittertwitter:card, twitter:* → og:* fallback1200×630 (min 300×157), < 5 MB
- Facebookog:title, og:description, og:image1200×630, 1.91:1
- LinkedInog:title, og:image (og: only)1200×627, 1.91:1
- Discordog:site_name, og:*, theme-color, twitter:cardLarge image needs summary_large_image
- Slackog:site_name, og:title, og:description, og:image1200×630
- WhatsAppog:title, og:description, og:imageKeep < 300 KB
Guidance based on each platform's public documentation and observed behavior. Platforms change their rules without notice.
FAQ
+Is Peekly free?
Yes. Checking a single URL is free forever — no signup and no limit on one-off checks. Batch watching and monitoring may arrive later as a paid Pro plan; the free single-URL tool stays.
+Why does my link show no image when I share it?
Usually one of four things: there's no og:image tag, the image URL is relative instead of absolute, the image is behind a login, firewall or bot block, or the file is too big or in an unsupported format like SVG. Peekly checks all four and tells you which one it is.
+What size should my og:image be?
1200×630 pixels (a 1.91:1 ratio) as a PNG or JPG under about 300 KB. That size works for large cards on X, Facebook, LinkedIn, Discord, Slack and WhatsApp.
+Do I need twitter: tags if I already have Open Graph tags?
X reads og:title, og:description and og:image as fallbacks, but you still need twitter:card set to summary_large_image if you want the big image card. Without it, X shows a small thumbnail.
+I fixed my tags but the old preview still shows. Why?
Platforms cache link previews, sometimes for days. Facebook and LinkedIn let you force a refresh with their own debugging tools. On X, Slack and Discord the cache usually expires on its own, or you can add a query string such as ?v=2 to the URL.
+Do you store the pages I check?
No. We fetch the page once to read its <head>, show you the result, and don't save the page or the result. We only fetch public http(s) URLs, and private or internal addresses are blocked.
+Are the previews pixel-perfect?
They're close approximations of each platform's current card layout, built from the same tags the platforms read. Platforms change their designs from time to time, so treat them as a very good guide rather than a screenshot.
$9
One time
A written fix for the batch.
Up to 25 URLs. We check og:image, Twitter cards, size, redirects, and cache, then send copy-paste meta tags within 48 hours. The free single-URL check stays free.