Loader Data
Pages often need data that isn’t stored in the page itself: the current locale, the signed-in user, the product for /products/[handle], or documents a block points at. Load it in your route’s loader, the same place you load the page, and pass it to React Editor as loaderData. Every component receives it next to its props.
<Render config={config} data={page} loaderData={{ locale: "fr", product }} />loaderData is never saved to the page data. It’s passed as a prop rather than through React context, so it works the same in the client renderer and in React Server Components.
Loading it
Use your framework’s own loader, and pass the result to <Render> on published pages and <Editor> on the editor route.
Next.js (App Router)
import { Render } from "@reacteditor/core/rsc";
import { config } from "@/editor.config";
export default async function Page({ params }) {
const { handle } = await params;
const [page, product] = await Promise.all([
getPage(`/products/${handle}`),
getProduct(handle),
]);
return <Render config={config} data={page} loaderData={{ product }} />;
}TanStack Start
Loaders also run in the browser on client-side navigation, so wrap server-only reads in a server function:
const getProductPage = createServerFn({ method: "GET" })
.inputValidator((handle: string) => handle)
.handler(async ({ data: handle }) => ({
page: await getPage(`/products/${handle}`),
product: await getProduct(handle),
}));
export const Route = createFileRoute("/products/$handle")({
loader: ({ params }) => getProductPage({ data: params.handle }),
component: () => {
const { page, product } = Route.useLoaderData();
return <Render config={config} data={page} loaderData={{ product }} />;
},
});The editor route
Load the same data for the page being edited, so blocks render the way they will once published:
<Editor config={config} data={page} loaderData={{ product }} />Reading it
In render
const config = {
components: {
ProductTitle: {
render: ({ loaderData }) => <h1>{loaderData.product?.title}</h1>,
},
},
};loaderData is reserved, so don’t define a field called loaderData. React Editor warns in development if a component does.
In resolveData and resolveFields
Both receive it as params.loaderData. When you run resolveAllData on the server, pass it as the third argument:
const resolved = await resolveAllData(page, config, { product });In field transforms
Field transforms receive loaderData too, which lets a field store a reference and the component receive what it points at. Pass the same transforms to <Editor> and <Render>:
const fieldTransforms = {
// A "product" field stores a handle; render receives the product
product: ({ value, loaderData }) => loaderData.products?.[value] ?? null,
};
<Render
config={config}
data={page}
loaderData={{ products }}
fieldTransforms={fieldTransforms}
/>;In the editor, a plugin’s useLoaderData can fetch that data for whatever blocks are on the page while you edit.
Typing it
loaderData is typed as LoaderData, an interface you can augment:
declare module "@reacteditor/core" {
interface LoaderData {
locale?: string;
product?: Product;
}
}Mark keys optional: not every route loads everything.