remix-run / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.

Home Page:https://remix.run

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

MetaFunction data type not inferred from clientLoader

cherewaty opened this issue · comments

Reproduction

https://stackblitz.com/edit/remix-run-remix-qggawd?file=app%2Froutes%2Fhello.tsx

System Info

System:
    OS: macOS 14.4.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 249.42 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.10.0 - ~/.nvm/versions/node/v20.10.0/bin/node
    Yarn: 4.1.1 - ~/.nvm/versions/node/v20.10.0/bin/yarn
    npm: 10.2.3 - ~/.nvm/versions/node/v20.10.0/bin/npm
  Browsers:
    Chrome: 122.0.6261.94
    Safari: 17.4.1

Used Package Manager

yarn

Expected Behavior

The docs for meta describe a TypeScript-friendly approach for using loader data in meta: https://remix.run/docs/en/main/route/meta#data

I have a Remix app in SPA mode using clientLoaders. I tried to take a similar approach to use typed clientLoader data in meta:

export async function clientLoader({ params }: ClientLoaderFunctionArgs) {
  return { task: { name: "Hello" } };
}

export const meta: MetaFunction<typeof clientLoader> = ({ data }) => {
  return [{ title: data.task.name }];
};

Actual Behavior

The code posted successfully passes the task with name Hello to meta, and it gets applied to the document title in the browser. But TypeScript reports that 'data' is of type 'unknown' inside of meta.

data: Loader extends LoaderFunction ? SerializeFrom<Loader> : unknown;

I believe it's because the generic only allows to extends from LoaderFunction.

commented

Yeah, also it makes sense.
You're rendering meta tags on the server, so in this case you should only rely on data provided by the loader.

Yeah, also it makes sense. You're rendering meta tags on the server, so in this case you should only rely on data provided by the loader.

This might not be the whole story since Remix also supports a SPA mode, I believe the meta function should work with clientLoader as well.

I've got a clunky workaround in place that gets me correct type information for data, but:

  • doesn't add type protection for the return of meta, since MetaFunction doesn't match
  • Requires importing a type from @remix-run/node, which feels conceptually wrong for SPA mode
import { SerializeFrom } from "@remix-run/node";

export const meta = ({ data }: { data: SerializeFrom<typeof clientLoader> }) => {
  return [{ title: data.task.name }];
};