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?
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
.