SolidStart Open Graph Meta Tags: Add OG to Your SolidJS App
How to add og:title, og:image, og:description meta tags in SolidStart using <Title>, <Meta>, and server-side rendering with createServerData$.
Why SolidStart is great for OG tags
SolidStart is the official meta-framework for SolidJS, and it ships with server-side rendering enabled by default. That matters enormously for Open Graph tags: social crawlers from Twitter, LinkedIn, Discord, and Slack never execute JavaScript — they parse the raw HTML response. If your OG tags are injected by client-side JS, crawlers will never see them.
SolidStart uses a file-based router and provides a <Title> and <Meta> component (from @solidjs/meta) that are streamed into the <head> during SSR. This guide shows you how to use them for both static and dynamic Open Graph tags.
Static OG tags with <Title> and <Meta>
For pages with known, fixed content, you can hard-code your OG tags using the @solidjs/meta primitives directly in your route component:
// src/routes/index.tsx
import { Title, Meta, Link as MetaLink } from "@solidjs/meta";
export default function HomePage() {
return (
<>
<Title>My SolidStart App — Fast and Resumable</Title>
<Meta name="description" content="Build instant web apps with SolidJS." />
{/* Open Graph */}
<Meta property="og:type" content="website" />
<Meta property="og:title" content="My SolidStart App — Fast and Resumable" />
<Meta property="og:description" content="Build instant web apps with SolidJS." />
<Meta property="og:image" content="https://yourdomain.com/og/home.png" />
<Meta property="og:url" content="https://yourdomain.com/" />
{/* Twitter Card */}
<Meta name="twitter:card" content="summary_large_image" />
<Meta name="twitter:title" content="My SolidStart App — Fast and Resumable" />
<Meta name="twitter:description" content="Build instant web apps with SolidJS." />
<Meta name="twitter:image" content="https://yourdomain.com/og/home.png" />
<MetaLink rel="canonical" href="https://yourdomain.com/" />
<main>
<h1>Welcome</h1>
</main>
</>
);
}SolidStart automatically hoists <Title>, <Meta>, and <Link> tags into the document <head> during SSR. The rendered HTML that reaches the browser (and crawlers) will contain all your tags in the correct location.
Dynamic OG tags with createAsync (SolidStart v1)
For blog posts, product pages, or any route with data-driven OG tags, use createAsync with a cached server function to fetch content before rendering. In SolidStart v1 (stable), server data functions replace the older createServerData$ API.
// src/routes/blog/[slug].tsx
import { Title, Meta } from "@solidjs/meta";
import { createAsync, cache } from "@solidjs/router";
import { useParams } from "@solidjs/router";
// Cached server function — runs on the server during SSR
const getPost = cache(async (slug: string) => {
"use server";
const res = await fetch(`https://api.yourdomain.com/posts/${slug}`);
if (!res.ok) throw new Error("Post not found");
return res.json() as Promise<{ title: string; excerpt: string; ogImage: string }>;
}, "post");
export const route = {
load: ({ params }: { params: { slug: string } }) => getPost(params.slug),
};
export default function BlogPost() {
const params = useParams();
const post = createAsync(() => getPost(params.slug));
return (
<>
<Title>{post()?.title ?? "Loading..."} | My Blog</Title>
<Meta name="description" content={post()?.excerpt ?? ""} />
<Meta property="og:type" content="article" />
<Meta property="og:title" content={post()?.title ?? ""} />
<Meta property="og:description" content={post()?.excerpt ?? ""} />
<Meta property="og:image" content={post()?.ogImage ?? ""} />
<Meta property="og:url" content={`https://yourdomain.com/blog/${params.slug}`} />
<Meta name="twitter:card" content="summary_large_image" />
<Meta name="twitter:image" content={post()?.ogImage ?? ""} />
<article>
<h1>{post()?.title}</h1>
<p>{post()?.excerpt}</p>
</article>
</>
);
}The key is the "use server" directive inside the cache function. This ensures the data fetch runs on the server during SSR, making the OG values available in the initial HTML response — exactly what social crawlers need.
Global OG defaults in app.tsx
Set site-wide OG defaults in your root layout. Per-page overrides will take precedence when a child route renders its own <Meta> tags (SolidStart uses last-write-wins for duplicate meta properties).
// src/app.tsx (or src/root.tsx)
import { MetaProvider, Title, Meta } from "@solidjs/meta";
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
export default function App() {
return (
<Router
root={(props) => (
<MetaProvider>
{/* Defaults — overridden by child routes */}
<Title>My SolidStart App</Title>
<Meta property="og:site_name" content="My SolidStart App" />
<Meta name="twitter:card" content="summary_large_image" />
<Meta name="twitter:site" content="@yourhandle" />
{props.children}
</MetaProvider>
)}
>
<FileRoutes />
</Router>
);
}OG image requirements and testing
Your og:image URL must follow these rules for all platforms to render it correctly:
- Absolute URL — always include the full domain (e.g.,
https://yourdomain.com/og/image.png). Relative paths are never resolved by crawlers. - HTTPS — HTTP image URLs are blocked by most platforms.
- 1200×630 px — the recommended dimensions for
summary_large_imageon Twitter. - Under 5 MB — LinkedIn and Twitter reject larger files.
- Public access — no auth required to fetch the image URL.
After deploying your SolidStart app, paste a URL into OGFixer to instantly preview how it looks on Twitter, LinkedIn, Discord, and Slack.
Check your SolidStart OG tags free
Paste any SolidStart URL into OGFixer to see live previews on Twitter, LinkedIn, Discord, and Slack — no login required.