From bd2ecd475b441d9c2b38c2f69d30e15ffb487eba Mon Sep 17 00:00:00 2001 From: DuroCodes Date: Sat, 21 Mar 2026 18:21:24 -0400 Subject: [PATCH] feat: add SEO stuff --- src/app/[id]/page.tsx | 45 +++++++++++++++++++++++++++++++++++++------ src/app/layout.tsx | 22 +++++++++++++++++++-- src/app/robots.ts | 12 ++++++++++++ src/app/sitemap.ts | 13 +++++++++++++ src/constants/site.ts | 1 + 5 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 src/app/robots.ts create mode 100644 src/app/sitemap.ts create mode 100644 src/constants/site.ts diff --git a/src/app/[id]/page.tsx b/src/app/[id]/page.tsx index 347915d..6f02b02 100644 --- a/src/app/[id]/page.tsx +++ b/src/app/[id]/page.tsx @@ -3,6 +3,7 @@ import { EditorProvider } from "~/components/editor-provider"; import { MonacoEditor } from "~/components/monaco-editor"; import { getPasteById } from "~/actions/paste"; import { Header } from "~/components/header"; +import { SITE_URL } from "~/constants/site"; import { LANGUAGES_SET, type LanguageName } from "~/utils/languages"; import { createEmptyTab, normalizeTabs } from "~/utils/paste-tabs"; @@ -51,13 +52,27 @@ export default async function PastePage({ params }: Props) { export async function generateMetadata({ params }: Props) { const { id } = await params; const paste = await getPasteById(id); + const ogImage = { url: "/sponge.png", alt: "spongebin" }; if (!paste) return { title: "spongebin", description: "a pastebin made with sponge", - openGraph: { images: "/sponge.png" }, - twitter: { card: "summary" }, + openGraph: { + type: "website", + locale: "en_US", + url: SITE_URL, + siteName: "spongebin", + title: "spongebin", + description: "a pastebin made with sponge", + images: [ogImage], + }, + twitter: { + card: "summary", + title: "spongebin", + description: "a pastebin made with sponge", + images: ["/sponge.png"], + }, }; const tabs = normalizeTabs(paste.tabs); @@ -66,10 +81,28 @@ export async function generateMetadata({ params }: Props) { ? tabs.reduce((sum, tab) => sum + tab.content.split("\n").length, 0) : paste.content.split("\n").length; + const title = `spongebin • ${paste.id}`; + const description = `a paste containing ${totalTabs} file${totalTabs === 1 ? "" : "s"} and ${totalLines} lines`; + const canonical = `${SITE_URL}/${paste.id}`; + return { - title: `spongebin • ${paste.id}`, - description: `a paste containing ${totalTabs} file${totalTabs === 1 ? "" : "s"} and ${totalLines} lines`, - openGraph: { images: "/sponge.png" }, - twitter: { card: "summary" }, + title, + description, + alternates: { canonical }, + openGraph: { + type: "website", + locale: "en_US", + url: canonical, + siteName: "spongebin", + title, + description, + images: [ogImage], + }, + twitter: { + card: "summary", + title, + description, + images: ["/sponge.png"], + }, }; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ece62a6..d7d7b9f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,12 +2,30 @@ import "./globals.css"; import type { Metadata, Viewport } from "next"; import { Analytics } from "@vercel/analytics/next"; import { Toaster } from "~/components/ui/sonner"; +import { SITE_URL } from "~/constants/site"; + +const site = new URL(SITE_URL); export const metadata: Metadata = { + metadataBase: site, + alternates: { canonical: SITE_URL }, title: "spongebin", description: "a pastebin made with sponge", - openGraph: { images: "/sponge.png" }, - twitter: { card: "summary" }, + openGraph: { + type: "website", + locale: "en_US", + url: SITE_URL, + siteName: "spongebin", + title: "spongebin", + description: "a pastebin made with sponge", + images: [{ url: "/sponge.png", alt: "spongebin" }], + }, + twitter: { + card: "summary", + title: "spongebin", + description: "a pastebin made with sponge", + images: ["/sponge.png"], + }, }; export const viewport: Viewport = { diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..2998cd8 --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,12 @@ +import type { MetadataRoute } from "next"; +import { SITE_URL } from "~/constants/site"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + }, + sitemap: `${SITE_URL}/sitemap.xml`, + }; +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..4d3846b --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,13 @@ +import type { MetadataRoute } from "next"; +import { SITE_URL } from "~/constants/site"; + +export default function sitemap(): MetadataRoute.Sitemap { + return [ + { + url: SITE_URL, + lastModified: new Date(), + changeFrequency: "weekly", + priority: 1, + }, + ]; +} diff --git a/src/constants/site.ts b/src/constants/site.ts new file mode 100644 index 0000000..15ce7b6 --- /dev/null +++ b/src/constants/site.ts @@ -0,0 +1 @@ +export const SITE_URL = "https://spongebin.dev";