Problem
app/layout.tsx sets:
export const metadata: Metadata = {
metadataBase: new URL("http://localhost:3000"),
...
}
Next.js resolves every relative Open Graph and Twitter image URL against
metadataBase. Deployed, this emits absolute URLs pointing at
http://localhost:3000 — so app/opengraph-image.tsx and
app/twitter-image.tsx (both of which exist and render) resolve to a host the
crawler cannot reach.
Every link shared to Twitter, LinkedIn, Slack, or Discord renders without a
preview card. For a project whose reach depends on grant reviewers and
hackathon judges following a link, this is a visible own-goal.
What to do
- Drive
metadataBase from an environment variable
(NEXT_PUBLIC_SITE_URL), falling back to the Vercel-provided
VERCEL_URL for previews and to localhost only in development.
- Add
openGraph.url and a canonical URL.
- Verify with a real crawler once deployed.
Acceptance criteria
Notes
Also worth setting metadata.title.template per route so shared deep links
carry a meaningful title rather than the site default.
Problem
app/layout.tsxsets:Next.js resolves every relative Open Graph and Twitter image URL against
metadataBase. Deployed, this emits absolute URLs pointing athttp://localhost:3000— soapp/opengraph-image.tsxandapp/twitter-image.tsx(both of which exist and render) resolve to a host thecrawler cannot reach.
Every link shared to Twitter, LinkedIn, Slack, or Discord renders without a
preview card. For a project whose reach depends on grant reviewers and
hackathon judges following a link, this is a visible own-goal.
What to do
metadataBasefrom an environment variable(
NEXT_PUBLIC_SITE_URL), falling back to the Vercel-providedVERCEL_URLfor previews and to localhost only in development.openGraph.urland a canonical URL.Acceptance criteria
metadataBaseresolved from the environment per deploymentNotes
Also worth setting
metadata.title.templateper route so shared deep linkscarry a meaningful title rather than the site default.