Remix Open Graph Meta Tags: Server-Rendered Meta with meta() Function

Add og:title, og:image, og:description, and Twitter card tags to Remix apps using the meta() export function — with dynamic data from loaders.

Why Remix is great for OG tags

Remix is a full-stack web framework built on server-side rendering. Every route renders on the server, which means social crawlers receive fully hydrated HTML with your <meta> tags already in place. You never have to worry about JavaScript not executing — Remix handles this by design.

Meta tags in Remix are managed through the meta export function at the route level. Each route can define its own metadata, and Remix merges them with parent route meta.

Basic meta() export

Export a meta function from any route file. It receives the loader data and returns an array of meta descriptor objects:

// app/routes/_index.tsx
import type { MetaFunction } from "@remix-run/node";

export const meta: MetaFunction = () => {
  return [
    { title: "My Remix Site" },
    { name: "description", content: "My site description." },
    { property: "og:title", content: "My Remix Site" },
    { property: "og:description", content: "My site description." },
    { property: "og:image", content: "https://example.com/og.jpg" },
    { property: "og:url", content: "https://example.com" },
    { property: "og:type", content: "website" },
    { name: "twitter:card", content: "summary_large_image" },
    { name: "twitter:title", content: "My Remix Site" },
    { name: "twitter:description", content: "My site description." },
    { name: "twitter:image", content: "https://example.com/og.jpg" },
  ];
};

export default function Index() {
  return <main>...</main>;
}

Dynamic OG tags from loader data

For dynamic pages like blog posts or product pages, load the data in a loader function and access it inside meta via the data parameter:

// app/routes/posts.$slug.tsx
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export async function loader({ params }: LoaderFunctionArgs) {
  const post = await getPost(params.slug);
  if (!post) throw new Response("Not Found", { status: 404 });
  return json({ post });
}

export const meta: MetaFunction<typeof loader> = ({ data }) => {
  if (!data) return [{ title: "Post Not Found" }];
  const { post } = data;
  return [
    { title: post.title },
    { name: "description", content: post.excerpt },
    { property: "og:title", content: post.title },
    { property: "og:description", content: post.excerpt },
    { property: "og:image", content: post.coverImage },
    { property: "og:url", content: `https://example.com/posts/${post.slug}` },
    { property: "og:type", content: "article" },
    { name: "twitter:card", content: "summary_large_image" },
  ];
};

export default function PostPage() {
  const { post } = useLoaderData<typeof loader>();
  return <article>...</article>;
}

Root route and default meta

Set a sensible default in app/root.tsx so every page has fallback meta tags. Child routes can override or extend these:

// app/root.tsx
export const meta: MetaFunction = () => [
  {
    name: "viewport",
    content: "width=device-width,initial-scale=1",
  },
  { charset: "utf-8" },
  { property: "og:type", content: "website" },
  { name: "twitter:card", content: "summary_large_image" },
];

Common Remix OG tag mistakes

  • Relative image URLs: og:image must be an absolute HTTPS URL. Use a base URL environment variable: `${process.env.BASE_URL}/og.jpg`.
  • Missing meta merging: in Remix v2, child meta replaces parent meta by default. Use matches to merge parent meta explicitly if needed.
  • Image dimensions: serve a 1200×630 px image for best rendering across platforms.
  • Forgetting og:url: always include a canonical URL to prevent duplicate content issues.

Test your Remix OG tags

Deploy to a public URL and verify each page with platform-specific tools:

Preview your OG tags free at OGFixer.com → Paste your Remix URL to see how it renders on Twitter, LinkedIn, Discord, and Slack.

← All guides