Search code examples
reactjstypescriptremix.run

Why does returning plain object from a loader in Remix work?


When using loaders in Remix, one should -- according to the documentation -- return a response object:

export async function loader() {
  const users = await db.users.findMany();
  const body = JSON.stringify(users);
  return new Response(body, {
    headers: {
      "Content-Type": "application/json",
    },
  });
}

There is also a helper function json, that simplifies the code:

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

export const loader = async () => {
  const users = await fakeDb.users.findMany();
  return json(users);
};

By mistake, I forgot to wrap the returned object in a response:

export const loader = async () => {
  const users = await fakeDb.users.findMany();
  return users;
};

I got no type error though and the loader worked just fine with useLoaderData<typeof loadRouteData>().

I assume, that the object is automatically serialized and wrapped in a Response object(?).

Is this a behavior I can rely on? Or is this considered bad practice and I should wrap the returned object in json like in the documentation?


Solution

  • Remix will automatically convert a naked object returned from your loader or action to JSON. The json helper function is optional but useful when sending additional headers and status code.

    If you enable Single Fetch, then you should always return a naked object, as this data will be bundled together with the other loader data. It actually serializes the data in a native format called turbo-stream, which supports non-JSON types like Date, BigInt, and even Promises.

    https://remix.run/docs/en/main/guides/single-fetch