Deno Open Graph Meta Tags: Add OG to Fresh and Deno Serve Apps
How to set og:title, og:image, og:description and Twitter card meta tags in Deno apps — using Fresh framework routes or vanilla Deno.serve.
Deno Fresh: using the Head component
Fresh (Deno's full-stack framework) renders pages server-side on every request. Use the <Head> component from $fresh/runtime.ts to inject OG meta tags into the HTML <head> — they'll be present in the raw response that social scrapers read.
// routes/blog/[slug].tsx
import { Head } from "$fresh/runtime.ts";
import { Handlers, PageProps } from "$fresh/server.ts";
interface Post {
title: string;
excerpt: string;
slug: string;
}
export const handler: Handlers<Post> = {
async GET(req, ctx) {
const slug = ctx.params.slug;
const res = await fetch(`https://api.example.com/posts/${slug}`);
if (!res.ok) return ctx.renderNotFound();
const post = await res.json();
return ctx.render(post);
},
};
export default function BlogPost({ data }: PageProps<Post>) {
const ogImage = `https://yourdomain.com/og/${data.slug}.png`;
const url = `https://yourdomain.com/blog/${data.slug}`;
return (
<>
<Head>
<title>{data.title} | My Blog</title>
<meta property="og:title" content={data.title} />
<meta property="og:description" content={data.excerpt} />
<meta property="og:image" content={ogImage} />
<meta property="og:url" content={url} />
<meta property="og:type" content="article" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={data.title} />
<meta name="twitter:description" content={data.excerpt} />
<meta name="twitter:image" content={ogImage} />
<link rel="canonical" href={url} />
</Head>
<main>
<h1>{data.title}</h1>
<p>{data.excerpt}</p>
</main>
</>
);
}Fresh: global default OG in _app.tsx
// routes/_app.tsx
import { AppProps } from "$fresh/server.ts";
import { Head } from "$fresh/runtime.ts";
export default function App({ Component }: AppProps) {
return (
<>
<Head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* Default OG fallbacks */}
<meta property="og:site_name" content="My App" />
<meta property="og:title" content="My App" />
<meta property="og:description" content="Default description." />
<meta property="og:image" content="https://yourdomain.com/og/default.png" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
</Head>
<Component />
</>
);
}Vanilla Deno.serve: string template approach
If you're not using Fresh, inject OG tags directly into your HTML string response:
// main.ts
function ogHead(title: string, description: string, image: string, url: string): string {
return `
<meta property="og:title" content="${title}" />
<meta property="og:description" content="${description}" />
<meta property="og:image" content="${image}" />
<meta property="og:url" content="${url}" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="${title}" />
<meta name="twitter:image" content="${image}" />
`;
}
Deno.serve(async (req: Request) => {
const url = new URL(req.url);
if (url.pathname.startsWith("/blog/")) {
const slug = url.pathname.replace("/blog/", "");
const post = await fetchPost(slug); // your data fetch
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<title>${post.title}</title>
${ogHead(post.title, post.excerpt, `https://yourdomain.com/og/${slug}.png`, req.url)}
</head>
<body>
<h1>${post.title}</h1>
</body>
</html>`;
return new Response(html, {
headers: { "content-type": "text/html; charset=utf-8" },
});
}
return new Response("Not found", { status: 404 });
});Deno Deploy considerations
When deploying to Deno Deploy, ensure your OG image URLs are absolute and publicly accessible. Deno Deploy serves from the edge — if you're generating OG images dynamically, use a stable CDN URL or a Deno Deploy edge function that returns the PNG image with the correct Content-Type: image/png header.
Verify your Deno OG tags
After deploying, paste your URL into OGFixer to preview how social platforms render your Deno app links and catch any OG tag issues before they go live.