diff --git a/landing/app/blog/[[...slug]]/page.tsx b/landing/app/blog/[[...slug]]/page.tsx
index e3d99cdb84..24f3f76ee0 100644
--- a/landing/app/blog/[[...slug]]/page.tsx
+++ b/landing/app/blog/[[...slug]]/page.tsx
@@ -7,6 +7,7 @@ import Link from "next/link";
import { notFound } from "next/navigation";
import { BlogLeftPanel } from "@/components/blog/blog-left-panel";
import { Callout } from "@/components/ui/callout";
+import { createMetadata } from "@/lib/metadata";
import { blogs } from "@/lib/source";
import { cn } from "@/lib/utils";
@@ -270,10 +271,10 @@ export async function generateMetadata({
}) {
const { slug } = await params;
if (!slug) {
- return {
+ return createMetadata({
title: "Blog - Better Auth",
description: "Latest updates, articles, and insights about Better Auth",
- };
+ });
}
const page = blogs.getPage(slug);
if (!page || page.data.draft) return notFound();
@@ -296,7 +297,7 @@ export async function generateMetadata({
const ogImage = image || ogUrl;
- return {
+ return createMetadata({
title,
description,
openGraph: {
@@ -311,7 +312,7 @@ export async function generateMetadata({
description,
images: [ogImage],
},
- };
+ });
}
export function generateStaticParams() {
diff --git a/landing/app/blog/layout.tsx b/landing/app/blog/layout.tsx
index a4aea65ce8..229959e9ce 100644
--- a/landing/app/blog/layout.tsx
+++ b/landing/app/blog/layout.tsx
@@ -1,21 +1,34 @@
import { RootProvider } from "fumadocs-ui/provider/next";
import type { Metadata } from "next";
+import { createMetadata } from "@/lib/metadata";
-export const metadata: Metadata = {
- title: "Blog - Better Auth",
- description: "Latest updates, articles, and insights about Better Auth",
+const description = "Latest updates, articles, and insights about Better Auth";
+
+export const metadata: Metadata = createMetadata({
+ title: "Blog",
+ description,
openGraph: {
+ url: "/blog",
title: "Blog - Better Auth",
- description: "Latest updates, articles, and insights about Better Auth",
+ description,
images: ["/api/og-release?heading=Better%20Auth%20Blog"],
},
twitter: {
- card: "summary_large_image",
- title: "Blog - Better Auth",
- description: "Latest updates, articles, and insights about Better Auth",
images: ["/api/og-release?heading=Better%20Auth%20Blog"],
+ title: "Blog - Better Auth",
+ description,
},
-};
+ alternates: {
+ types: {
+ "application/rss+xml": [
+ {
+ title: "Better Auth Blog",
+ url: "https://better-auth.com/blog/rss.xml",
+ },
+ ],
+ },
+ },
+});
export default function BlogLayout({
children,
diff --git a/landing/app/careers/page.tsx b/landing/app/careers/page.tsx
index 3f8eabcc66..c08e17ba97 100644
--- a/landing/app/careers/page.tsx
+++ b/landing/app/careers/page.tsx
@@ -1,10 +1,11 @@
import type { Metadata } from "next";
+import { createMetadata } from "@/lib/metadata";
import { CareersPageClient } from "./careers-client";
-export const metadata: Metadata = {
+export const metadata: Metadata = createMetadata({
title: "Careers",
description: "Join the Better Auth team — open positions and how to apply.",
-};
+});
export default function CareersPage() {
return ;
diff --git a/landing/app/changelog/page.tsx b/landing/app/changelog/page.tsx
index 57090d91ef..b9c9ebfcf6 100644
--- a/landing/app/changelog/page.tsx
+++ b/landing/app/changelog/page.tsx
@@ -1,5 +1,6 @@
import Link from "next/link";
import { HalftoneBackground } from "@/components/landing/halftone-bg";
+import { createMetadata } from "@/lib/metadata";
import { ChangelogContent } from "./changelog-content";
export const dynamic = "force-static";
@@ -148,7 +149,7 @@ export default async function ChangelogPage() {
);
}
-export const metadata = {
- title: "Changelog - Better Auth",
+export const metadata = createMetadata({
+ title: "Changelog",
description: "Latest changes, fixes, and updates to Better Auth",
-};
+});
diff --git a/landing/app/community/page.tsx b/landing/app/community/page.tsx
index 87381810be..0812e0dc7c 100644
--- a/landing/app/community/page.tsx
+++ b/landing/app/community/page.tsx
@@ -1,12 +1,13 @@
import type { Metadata } from "next";
import { getCommunityStats } from "@/lib/community-stats";
+import { createMetadata } from "@/lib/metadata";
import { CommunityPageClient } from "./community-client";
-export const metadata: Metadata = {
+export const metadata: Metadata = createMetadata({
title: "Community",
description:
"Join the Better Auth community — contributors, Discord members, and ecosystem stats.",
-};
+});
export const revalidate = 21600; // Revalidate every 6 hours
diff --git a/landing/app/docs/[[...slug]]/page.tsx b/landing/app/docs/[[...slug]]/page.tsx
index c8a48c0fe6..902f7853bb 100644
--- a/landing/app/docs/[[...slug]]/page.tsx
+++ b/landing/app/docs/[[...slug]]/page.tsx
@@ -24,6 +24,7 @@ import {
GenerateSecret,
} from "@/components/docs/mdx-components";
import { Callout } from "@/components/ui/callout";
+import { createMetadata } from "@/lib/metadata";
import { getSource } from "@/lib/source";
import { cn } from "@/lib/utils";
import { LLMCopyButton, ViewOptions } from "./page.client";
@@ -164,7 +165,7 @@ export async function generateMetadata({
const ogUrl = `/api/og?${ogSearchParams.toString()}`;
- return {
+ return createMetadata({
title: page.data.title,
description: page.data.description,
openGraph: {
@@ -186,5 +187,5 @@ export async function generateMetadata({
description: page.data.description,
images: [ogUrl],
},
- };
+ });
}
diff --git a/landing/app/enterprise/page.tsx b/landing/app/enterprise/page.tsx
index dacddd4c05..d5e56aa0a6 100644
--- a/landing/app/enterprise/page.tsx
+++ b/landing/app/enterprise/page.tsx
@@ -1,11 +1,12 @@
import type { Metadata } from "next";
+import { createMetadata } from "@/lib/metadata";
import { EnterprisePageClient } from "./enterprise-client";
-export const metadata: Metadata = {
+export const metadata: Metadata = createMetadata({
title: "Enterprise",
description:
"Better Auth for enterprise — SSO, SAML, audit logs, and dedicated support.",
-};
+});
export default function EnterprisePage() {
return ;
diff --git a/landing/app/layout.tsx b/landing/app/layout.tsx
index 8d78b488b9..25b3466f9a 100644
--- a/landing/app/layout.tsx
+++ b/landing/app/layout.tsx
@@ -7,6 +7,7 @@ import Script from "next/script";
import type { ReactNode } from "react";
import { StaggeredNavFiles } from "@/components/landing/staggered-nav-files";
import { Providers } from "@/components/providers";
+import { createMetadata } from "@/lib/metadata";
const fontSans = Geist({
subsets: ["latin"],
@@ -18,43 +19,13 @@ const fontMono = Geist_Mono({
variable: "--font-mono",
});
-export const metadata: Metadata = {
- metadataBase: new URL(
- process.env.VERCEL_URL
- ? `https://${process.env.VERCEL_URL}`
- : process.env.NODE_ENV === "production"
- ? "https://better-auth.com"
- : (process.env.NEXT_PUBLIC_URL ?? "http://localhost:3000"),
- ),
+export const metadata: Metadata = createMetadata({
title: {
template: "%s | Better Auth",
default: "Better Auth",
},
description: "The Most Comprehensive Authentication Framework",
- icons: {
- icon: [
- { url: "/favicon/favicon.ico", sizes: "any" },
- {
- url: "/favicon/favicon-32x32.png",
- sizes: "32x32",
- type: "image/png",
- },
- {
- url: "/favicon/favicon-16x16.png",
- sizes: "16x16",
- type: "image/png",
- },
- ],
- apple: "/favicon/apple-touch-icon.png",
- },
- openGraph: {
- images: ["/og.png"],
- },
- twitter: {
- card: "summary_large_image",
- images: ["/og.png"],
- },
-};
+});
export default function RootLayout({ children }: { children: ReactNode }) {
return (
diff --git a/landing/app/products/[tab]/page.tsx b/landing/app/products/[tab]/page.tsx
index fbede66619..8c75ed7481 100644
--- a/landing/app/products/[tab]/page.tsx
+++ b/landing/app/products/[tab]/page.tsx
@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import { redirect } from "next/navigation";
+import { createMetadata } from "@/lib/metadata";
import { FrameworkContent } from "./_components/framework-content";
import { InfrastructureContent } from "./_components/infrastructure-content";
@@ -30,7 +31,7 @@ export async function generateMetadata({
const { tab } = await params;
const meta = tabs[tab as Tab];
if (!meta) return {};
- return { title: meta.title, description: meta.description };
+ return createMetadata({ title: meta.title, description: meta.description });
}
export default async function TabPage({
diff --git a/landing/lib/metadata.ts b/landing/lib/metadata.ts
new file mode 100644
index 0000000000..ae0a90fa2f
--- /dev/null
+++ b/landing/lib/metadata.ts
@@ -0,0 +1,47 @@
+import type { Metadata } from "next";
+
+export function createMetadata(override: Metadata): Metadata {
+ return {
+ ...override,
+ metadataBase: baseUrl,
+ openGraph: {
+ title: override.title ?? undefined,
+ description: override.description ?? undefined,
+ url: "https://better-auth.com",
+ images: "/og.png",
+ siteName: "Better Auth",
+ ...override.openGraph,
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: override.title ?? undefined,
+ description: override.description ?? undefined,
+ images: "/og.png",
+ ...override.twitter,
+ },
+ icons: {
+ icon: [
+ { url: "/favicon/favicon.ico", sizes: "any" },
+ {
+ url: "/favicon/favicon-32x32.png",
+ sizes: "32x32",
+ type: "image/png",
+ },
+ {
+ url: "/favicon/favicon-16x16.png",
+ sizes: "16x16",
+ type: "image/png",
+ },
+ ],
+ apple: "/favicon/apple-touch-icon.png",
+ },
+ };
+}
+
+export const baseUrl =
+ process.env.NODE_ENV === "development" ||
+ (!process.env.VERCEL_PROJECT_PRODUCTION_URL && !process.env.VERCEL_URL)
+ ? new URL("http://localhost:3000")
+ : new URL(
+ `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL || process.env.VERCEL_URL}`,
+ );
diff --git a/landing/lib/rss.ts b/landing/lib/rss.ts
index 93325ba1d9..85ebafd720 100644
--- a/landing/lib/rss.ts
+++ b/landing/lib/rss.ts
@@ -1,6 +1,6 @@
import { Feed } from "feed";
+import { baseUrl } from "./metadata";
import { blogs } from "./source";
-import { baseUrl } from "./utils";
export function getRSS() {
const feed = new Feed({
diff --git a/landing/lib/utils.ts b/landing/lib/utils.ts
index 8a90058be7..7d63e21d50 100644
--- a/landing/lib/utils.ts
+++ b/landing/lib/utils.ts
@@ -19,11 +19,3 @@ export function mergeRefs(
});
};
}
-
-export const baseUrl =
- process.env.NODE_ENV === "development" ||
- (!process.env.VERCEL_PROJECT_PRODUCTION_URL && !process.env.VERCEL_URL)
- ? new URL("http://localhost:3000")
- : new URL(
- `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL || process.env.VERCEL_URL}`,
- );