How toGet the absolute URL in Remix's MetaFunction

Let's say you want to add Open Graph image to a route in Remix, one first approach could be to import the image and use MetaFunction to add it:

app/routes/something.tsx
import og from "./og.png"; export const meta: MetaFunction = () => { return [ { property: "og:image", content: og } ]; }

The problem with this is that unless you have configured your publicPath in remix.config.mjs to use a full URL you're going to get a relative URL from the import, so og will be something like /build/_assets/og-HASH.png, and og:image needs to be absolute URLs.

One solution for that is to add the rest of the URL to the og.

{ property: "og:image", content: new URL(og, "https://sergiodxa.com").href }

Now it will be a full URL, but here we're always using the same domain, what happens if our apps handles multiple domains? Or when testing locally? It will use the incorrect URL.

You may want to reach for environment variable, so you can replace the hard-coded domain with that.

{ property: "og:image", content: new URL(og, process.env.PUBLIC_HOST).href }

But because MetaFunction runs client-side you can't use env variables there, so what's our option? Well, MetaFunction receives the loader data, so we can use our loader!

app/routes/something.tsx
export async function loader({ request }: LoaderFunctionArgs) { let ogUrl = new URL(og, request.url) return json({ ogUrl }) } export const meta: MetaFunction<typeof loader> = ({ data }) => { return [ { property: "og:image", content: data.ogUrl }, ]; }

Now our og:image will be a full URL computed from the request.url. But the loader may fail, so we have to handle that case too.

app/routes/something.tsx
export const meta: MetaFunction<typeof loader> = ({ data }) => { if (!data) return []; // return empty list if loader failed return [ { property: "og:image", content: data.ogUrl }, ]; }