Skip to content

Data Loading

Route loaders fetch data before the component renders. They integrate with the mesh resource system.

Loader

ts
{
  path: "/products/:id",
  component: () => import("./ProductDetail"),
  loader: async ({ params, mesh, signal }) => {
    return mesh.resource("product.detail").fetch({ id: params.id }, { signal });
  }
}

The loader receives:

  • params — path parameters
  • mesh — the mesh instance
  • signal — AbortSignal (aborted if navigation is cancelled)

Dependencies

Prefetch data in parallel with the main loader:

ts
{
  path: "/orders/:id",
  loader: async ({ params, mesh }) => mesh.resource("order.detail").fetch({ id: params.id }),
  dependencies: {
    customer: (params, mesh) => mesh.resource("customer.detail").fetch({ id: params.customerId }),
    products: (params, mesh) => mesh.resource("products.list").fetch()
  }
}

Dependencies run in parallel with the main loader. If data is already cached, the dependency resolves instantly.

Routes with rollback: true revert the entire navigation if the loader fails:

ts
{
  path: "/checkout",
  rollback: true,
  loader: async ({ mesh }) => {
    return mesh.resource("checkout.summary").fetch();
    // If this throws, the URL reverts to the previous route
  }
}

No broken page is ever shown. The user stays on the previous route.

Error Recovery

Auto-retry failed loaders:

ts
{
  path: "/dashboard",
  loader: async ({ mesh }) => mesh.resource("dashboard.data").fetch(),
  errorRecovery: {
    retry: 3,
    retryDelay: backoff({ base: 1000, max: 10000 }),
    fallbackComponent: () => import("./DashboardSkeleton"),
    onError: (error) => console.error("Dashboard load failed:", error)
  }
}

Pending UI

Show a loading state while the loader runs:

ts
{
  path: "/products",
  component: () => import("./Products"),
  pendingComponent: () => import("./ProductsLoading"),
  loader: async ({ mesh }) => mesh.resource("products.list").fetch()
}

The pendingComponent is shown after defaultPendingMs (default: 200ms) to avoid flash.

Error UI

Show a custom error component when the loader fails:

ts
{
  path: "/products/:id",
  component: () => import("./ProductDetail"),
  errorComponent: () => import("./ProductError"),
  loader: async ({ params, mesh }) => mesh.resource("product.detail").fetch({ id: params.id })
}

Important Notes

TIP

Loaders run before the component renders. The component receives loader data via useMatch().data.

WARNING

Always pass the loader's signal to your fetch calls. This ensures cancelled navigations don't waste network requests.

Next Steps

Released under the MIT License.