meta

The meta export will set meta tags for your html document. We highly recommend setting the title and description on every route besides layout routes (their index route will set the meta).

import type { MetaFunction } from "@remix-run/node"; // or cloudflare/deno

export const meta: MetaFunction = () => {
  return {
    title: "Something cool",
    description:
      "This becomes the nice preview on search results.",
  };
};

The meta function may run on the server (e.g. the initial page load) or the client (e.g. a client navigation), so you cannot access server-specific data like process.env.NODE_ENV directly. If you need server-side data in meta, get the data in the loader and access it via the meta function's data parameter.

There are a few special cases (read about those below). In the case of nested routes, the meta tags are merged automatically, so parent routes can add meta tags without the child routes needing to copy them.

HtmlMetaDescriptor

This is an object representation and abstraction of a <meta {...props}> element and its attributes. View the MDN docs for the meta API.

The meta export from a route should return a single HtmlMetaDescriptor object.

Almost every meta element takes a name and content attribute, with the exception of OpenGraph tags which use property instead of name. In either case, the attributes represent a key/value pair for each tag. Each pair in the HtmlMetaDescriptor object represents a separate meta element, and Remix maps each to the correct attributes for that tag.

The meta object can also hold a title reference which maps to the HTML <title> element.

As a convenience, charset: "utf-8" will render a <meta charset="utf-8">.

As a last option, you can also pass an object of attribute/value pairs as the value. This can be used as an escape-hatch for meta tags like the http-equiv tag which uses http-equiv instead of name.

Examples:

import type { MetaFunction } from "@remix-run/node"; // or cloudflare/deno

export const meta: MetaFunction = () => ({
  // Special cases
  charset: "utf-8", // <meta charset="utf-8">
  "og:image": "https://josiesshakeshack.com/logo.jpg", // <meta property="og:image" content="https://josiesshakeshack.com/logo.jpg">
  title: "Josie's Shake Shack", // <title>Josie's Shake Shack</title>

  // name => content
  description: "Delicious shakes", // <meta name="description" content="Delicious shakes">
  viewport: "width=device-width,initial-scale=1", // <meta name="viewport" content="width=device-width,initial-scale=1">

  // <meta {...value}>
  refresh: {
    httpEquiv: "refresh",
    content: "3;url=https://www.mozilla.org",
  }, // <meta http-equiv="refresh" content="3;url=https://www.mozilla.org">
});

Page context in meta function

meta function is passed an object that has following data:

export const meta: MetaFunction<typeof loader> = ({
  data,
  params,
}) => {
  if (!data) {
    return {
      title: "Missing Shake",
      description: `There is no shake with the ID of ${params.shakeId}. 😢`,
    };
  }

  const { shake } = data;
  return {
    title: `${shake.name} milkshake`,
    description: shake.summary,
  };
};

To infer types for parentsData, provide a mapping from the route's file path (relative to app/) to that route loader type:

// app/routes/sales.tsx
const loader = () => {
  return json({ salesCount: 1074 });
};
export type Loader = typeof loader;
import type { Loader as SalesLoader } from "../../sales";

const loader = () => {
  return json({ name: "Customer name" });
};

const meta: MetaFunction<
  typeof loader,
  {
    "routes/sales": SalesLoader;
  }
> = ({ data, parentsData }) => {
  const { name } = data;
  //      ^? string
  const { salesCount } = parentsData["routes/sales"];
  //      ^? number
};