{"id":34907,"date":"2026-09-08T19:45:38","date_gmt":"2026-09-08T17:45:38","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=34907"},"modified":"2026-09-09T09:49:07","modified_gmt":"2026-09-09T07:49:07","slug":"router-resources-loading-data-with-the-angular-router","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/","title":{"rendered":"Router Resources: Loading Data with the Angular Router"},"content":{"rendered":"<p><em>Signal-based resolvers: parallel instead of waterfall, blocking or non-blocking<\/em><\/p>\n<p>Resolvers are as old as the Angular Router itself. They load data before a route is activated and return an Observable or a Promise for it. In a world where data access increasingly goes through <code>resource<\/code>, <code>rxResource<\/code>, and <code>httpResource<\/code>, that feels out of place. With version 22.2.0-next.5, the router therefore gains an experimental alternative: <strong>Router Resources<\/strong>. They not only fit Signals better, they also bring a performance benefit, because the router loads them in parallel instead of one after another.<\/p>\n<p>This article explains the new API, shows both flavors using our demo application Flights42, and then looks into how Router Resources work together with Signal Stores. <\/p>\n<p>\ud83d\udcc2 <a href=\"https:\/\/github.com\/angular-architects\/flights42\/tree\/22.2.0-next.5\">Source code<\/a> (branch: <code>22.2.0-next.5<\/code>)<\/p>\n<h2>What Are Router Resources?<\/h2>\n<p>With Router Resources, a route gets a <code>resources<\/code> property that returns ordinary resources. The router takes care of their lifecycle and passes the results on to the component.<\/p>\n<p>The second reason for the new API is at least as important as the nicer interface: <strong>performance<\/strong>. The router processes classic resolvers sequentially along the route hierarchy. If the parent route needs 2 seconds for its data and the child route 3, the user waits 5 seconds for the navigation. Router Resources, by contrast, all start at the same time, even when they block. In the same example, the navigation now takes only 3 seconds, as long as the slowest resource:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/waterfall.png\" alt=\"Timing diagram: Two resolvers taking 2 and 3 seconds run one after another and block the navigation for 5 seconds. As Router Resources, both run in parallel and the navigation completes after 3 seconds.\" \/><\/p>\n<p>Besides such <strong>blocking Router Resources<\/strong>, Router Resources can also be defined as <strong>non-blocking<\/strong>. This means the router doesn't wait at all. It activates the route immediately and hands the still-loading resource to the component, which then renders the loading state itself.<\/p>\n<h2>Setting Up Router Resources<\/h2>\n<p>The feature is enabled via <code>withRouterResources<\/code> when configuring the router:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/app.config.ts\n\nimport {\n  \u0275withRouterResources as withRouterResources,\n  provideRouter,\n  withComponentInputBinding,\n} from &#039;@angular\/router&#039;;\n\n[...]\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    [...]\n    provideRouter(\n        routes, \n        withComponentInputBinding(), \n        withRouterResources()\n    ),\n  ],\n};<\/code><\/pre>\n<p>Router Resources are still experimental. That's why the imports start with the <code>\u0275<\/code> prefix: in 22.2.0-next.5, the router exports the new symbols only privately, so the demo renames them to their official names on import. In addition, <code>withComponentInputBinding<\/code> is used so that the router binds the results of the resources to the component as inputs.<\/p>\n<p>The <code>resources<\/code> property isn't part of the public <code>Route<\/code> type yet either. The runtime already evaluates it; for the compiler, the demo adds it via a small type declaration:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/domains\/shared\/util-common\/resource-route.d.ts\n\nimport { Resource } from &#039;@angular\/core&#039;;\nimport {\n  \u0275ResourceContext as ResourceContext,\n  \u0275ResourceResult as ResourceResult,\n} from &#039;@angular\/router&#039;;\n\ndeclare module &#039;@angular\/router&#039; {\n  interface Route {\n    resources?: (\n      ctx: ResourceContext,\n    ) =&gt; ResourceResult | Promise&lt;ResourceResult&gt;;\n  }\n\n  interface ActivatedRoute {\n    resources?: Record&lt;string, Resource&lt;unknown&gt; &amp; { reload(): boolean }&gt;;\n  }\n}<\/code><\/pre>\n<p>The second part of the declaration, <code>resources<\/code> on the <code>ActivatedRoute<\/code>, comes into play further below when reloading.<\/p>\n<h2>Blocking Router Resources<\/h2>\n<p>With these preparations in place, the first Router Resource can be defined. The route for editing a passenger loads the record before the component appears:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/domains\/ticketing\/ticketing.routes.ts\n\n{\n  path: &#039;passenger-edit\/:id&#039;,\n  component: PassengerEdit,\n  resources: (ctx) =&gt; ({\n    passenger: createSimplePassengerResource(ctx.params),\n  }),\n},<\/code><\/pre>\n<p>The <code>resources<\/code> function runs in an injection context and receives a <code>ResourceContext<\/code>. Among other things, it provides <code>params<\/code>, <code>queryParams<\/code>, and <code>data<\/code> as Signals. The factory function derives the id from them and creates a perfectly ordinary <code>httpResource<\/code>:<\/p>\n<pre><code class=\"language-typescript\">\/\/ ...\/feature-booking\/passenger-edit\/simple-passenger-resource.ts\n\nimport { computed, inject, Signal } from &#039;@angular\/core&#039;;\nimport { Params } from &#039;@angular\/router&#039;;\n\nimport { PassengerClient } from &#039;..\/..\/data\/passenger-client&#039;;\n\nexport function createSimplePassengerResource(params: Signal&lt;Params&gt;) {\n  const passengerClient = inject(PassengerClient);\n  const id = computed(() =&gt; Number(params()[&#039;id&#039;] ?? 0));\n  return passengerClient.findPassengerResourceById(id, {\n    withDefaultValue: false,\n  });\n}<\/code><\/pre>\n<p>Without any further ado, such a resource is <strong>blocking<\/strong>. Just like with a resolver, the router waits until the value is available and only then activates the route. Thanks to <code>withComponentInputBinding<\/code>, it's not the resource itself that ends up in the component, but the already unwrapped value:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/domains\/ticketing\/feature-booking\/passenger-edit\/passenger-edit.ts\n\n@Component({ [...] })\nexport class PassengerEdit {\n  [...]\n  protected readonly passenger = input.required&lt;Passenger&gt;();\n}<\/code><\/pre>\n<p>This is the same input a resolver populated before. For the component, the switch is invisible, which makes migration pleasant. If the route parameter changes later while the component is reused, the resource fetches the new value on its own, because <code>ctx.params<\/code> is a Signal.<\/p>\n<p>One detail deserves attention: the call above uses <code>withDefaultValue: false<\/code> to disable the <code>httpResource<\/code>'s default value. In 22.2.0-next.5, a resource <strong>with<\/strong> a default value doesn't block, because from the router's point of view a value is already present. This detail is expected to change, though. Until then, leave out <code>defaultValue<\/code> if the resource is supposed to block.<\/p>\n<h2>Non-Blocking Router Resources<\/h2>\n<p>Navigation shouldn't always wait for data. Often it's better to show the target page right away and make the loading state visible there. That's exactly what <code>nonBlocking<\/code> is for. The demo's luggage management uses it for its detail route:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/domains\/luggage\/feature-luggage\/luggage.routes.ts\n\nimport { \u0275nonBlocking as nonBlocking, Routes } from &#039;@angular\/router&#039;;\n\n[...]\n\n{\n  path: &#039;:id&#039;,\n  component: LuggageDetail,\n  resources: (ctx) =&gt; ({\n    luggage: nonBlocking(createLuggageResource(ctx.params)),\n  }),\n},<\/code><\/pre>\n<p>The factory function <code>createLuggageResource<\/code> is structured like the passenger example and delegates to the <code>LuggageClient<\/code>. Its loader is deliberately slow so the effect becomes visible:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/domains\/luggage\/data\/luggage-client.ts\n\nfindLuggageById(id: Signal&lt;number&gt;) {\n  return resource({\n    params: id,\n    loader: async ({ params: id }) =&gt; {\n      \/\/ Deliberately delayed to make the loading state visible\n      await new Promise((resolve) =&gt; setTimeout(resolve, 2000));\n      return this.getLuggage().find((item) =&gt; item.id === id);\n    },\n  });\n}<\/code><\/pre>\n<p>The router now activates the route immediately. In this case, the component doesn't receive the unwrapped value but the complete <code>Resource<\/code> as its input:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/domains\/luggage\/feature-luggage\/luggage-detail\/luggage-detail.ts\n\n@Component({ [...] })\nexport class LuggageDetail {\n  readonly luggage = input.required&lt;Resource&lt;Luggage | undefined&gt;&gt;();\n}<\/code><\/pre>\n<p>This makes all of the resource's state Signals available in the template, such as <code>isLoading<\/code> and <code>error<\/code>:<\/p>\n<pre><code class=\"language-html\">&lt;!-- src\/app\/domains\/luggage\/feature-luggage\/luggage-detail\/luggage-detail.html --&gt;\n\n@let resource = luggage();\n@let item = resource.value();\n\n@if (resource.isLoading()) {\n  &lt;p&gt;Loading luggage \u2026&lt;\/p&gt;\n} @else if (resource.error()) {\n  &lt;p&gt;Luggage could not be loaded.&lt;\/p&gt;\n} @else if (item) {\n  &lt;form&gt; [...] &lt;\/form&gt;\n}<\/code><\/pre>\n<p>The division of labor is clear: blocking resources deliver finished values and guarantee that the target page is complete. Non-blocking resources deliver the loading lifecycle along with the data and leave rendering the intermediate state to the target page.<\/p>\n<p><div style=\"\nmargin: 8px 0;\npadding: 22px;\nborder: 1px solid #e5e7eb;\nborder-radius: 14px;\nbackground: #f8fafc;\n\">NOTE<\/p>\n<h3 style=\"margin-top:0\">Modern Angular<\/h3>\n<\/p>\n<p style=\"margin:0 0 12px 0\"><strong style=\"display:inline-block; background:#16a34a; color:#fff; padding:4px 10px; border-radius:999px; font-size:0.85em\">\u2713 Already updated to Angular 22!<\/strong><\/p>\n<p>You'll find more on resources, Signals, and modern Angular architecture in my new eBook Modern Angular. It covers Signals, architecture, testing, AI assistants, and practical solutions for modern business applications.<\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/modern-book\"><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/01\/cover-klein.png\" width=\"400\" alt=\"Modern Angular - Signal-first, Architecture-first, Practice-first\" style=\"cursor:pointer !important\"><\/a><\/p>\n<p><a style=\"cursor:pointer !important\" href=\"https:\/\/www.angulararchitects.io\/modern-book\">More about the book \u2192<\/a>\n<\/div>\n<\/p>\n<h2>Reload and Redirect<\/h2>\n<p>Two more capabilities set Router Resources apart from resolvers. The first concerns reloading. Anyone who wanted to fetch a resolver's data again had to re-activate the route, for instance with <code>onSameUrlNavigation: &#039;reload&#039;<\/code>. That repeats the entire navigation, including guards and resolvers. A Router Resource, on the other hand, can be reloaded on its own, without the router starting a navigation.<\/p>\n<p>With a blocking resource, however, the component only receives the value, not the resource itself. Access to it is provided by the <code>ActivatedRoute<\/code>, which offers all of the route's resources under their keys:<\/p>\n<pre><code class=\"language-typescript\">\/\/ src\/app\/domains\/ticketing\/feature-booking\/passenger-edit\/passenger-edit.ts\n\nimport { ActivatedRoute } from &#039;@angular\/router&#039;;\n\n[...]\n\n@Component({ [...] })\nexport class PassengerEdit {\n  [...]\n  private readonly passengerResource =\n    inject(ActivatedRoute).resources?.[&#039;passenger&#039;];\n\n  protected reload(): void {\n    \/\/ Reloads only this resource without re-activating the route\n    this.passengerResource?.reload();\n  }\n}<\/code><\/pre>\n<p>The <code>?.<\/code> accounts for the fact that the demo can also run the same route with a resolver; in that case, there is no resource. A button in the template calls <code>reload<\/code>. Since the router binds the value of a blocking resource to the input via an effect, the new record arrives in the component on its own once loaded. In the demo, this also resets the form, because <code>passengerModel<\/code> is a <code>linkedSignal<\/code> on the input: local changes are discarded, and the record comes fresh from the server.<\/p>\n<p>The second capability concerns missing data. The <code>httpResource<\/code> from the blocking example reports an unknown passenger as an error, since the API responds with 404. With a blocking resource, the router then cancels the navigation and emits a <code>NavigationError<\/code>; the user stays on the previous page. Usually, a redirect is the better reaction, for example to a not-found page. For this, the loader throws a <code>RedirectCommand<\/code>. The router cancels the navigation and redirects to the given URL.<\/p>\n<p>Since <code>httpResource<\/code> has no loader of its own, the demo falls back on <code>resource<\/code> for this. The new factory function calls the <code>PassengerClient<\/code> and translates the 404 error into a redirect:<\/p>\n<pre><code class=\"language-typescript\">\/\/ ...\/feature-booking\/passenger-edit\/passenger-resource.ts\n\nimport { HttpErrorResponse } from &#039;@angular\/common\/http&#039;;\nimport { inject, resource, Signal } from &#039;@angular\/core&#039;;\nimport { Params, RedirectCommand, Router } from &#039;@angular\/router&#039;;\nimport { firstValueFrom } from &#039;rxjs&#039;;\n\nimport { PassengerClient } from &#039;..\/..\/data\/passenger-client&#039;;\n\nexport function createPassengerResource(params: Signal&lt;Params&gt;) {\n  const passengerClient = inject(PassengerClient);\n  const router = inject(Router);\n\n  return resource({\n    params: () =&gt; Number(params()[&#039;id&#039;] ?? 0),\n    loader: async ({ params: id }) =&gt; {\n      try {\n        return await firstValueFrom(passengerClient.findById(String(id)));\n      } catch (error) {\n        if (error instanceof HttpErrorResponse &amp;&amp; error.status === 404) {\n          \/\/ Cancels the navigation and redirects instead of failing it\n          throw new RedirectCommand(router.parseUrl(&#039;\/not-found&#039;));\n        }\n        throw error;\n      }\n    },\n  });\n}<\/code><\/pre>\n<p>In the demo, the route <code>passenger-edit\/:id<\/code> now uses this factory function instead of <code>createSimplePassengerResource<\/code>; the route <code>not-found<\/code> shows a plain notice page. Navigating to <code>passenger-edit\/999999<\/code> takes you there without the target page ever appearing. This only works with blocking resources, because only there is the router still waiting for the result. With non-blocking resources, the navigation has already completed, an error ends up in <code>error()<\/code>, and the component decides for itself how to react.<\/p>\n<h2>Router Resources and Signal Stores<\/h2>\n<p>In many applications, it's not the route that loads the data but a Signal Store. In the demo, the <code>PassengerDetailStore<\/code> manages the current passenger. It is given an id and then loads the record on its own via an embedded resource. How do you combine such a store with a blocking Router Resource?<\/p>\n<p>The demo shows the following solution. A hand-written resource sets the id on the store in its loader, waits until the store has finished loading, and then returns its value:<\/p>\n<pre><code class=\"language-typescript\">\/\/ ...\/feature-booking\/passenger-edit\/store-passenger-resource.ts\n\nexport function createStorePassengerResource(params: Signal&lt;Params&gt;) {\n  const store = inject(PassengerDetailStore);\n  const passengerLoaded = waitFor(store.passengerIsLoading, false);\n\n  return resource({\n    params: () =&gt; Number(params()[&#039;id&#039;] ?? 0),\n    loader: async ({ params: id, abortSignal }) =&gt; {\n      store.setPassengerId(id);\n      await passengerLoaded(abortSignal);\n      return store.passengerValue();\n    },\n  });\n}<\/code><\/pre>\n<p>The helper function <a href=\"https:\/\/github.com\/angular-architects\/flights42\/blob\/22.2.0-next.5\/src\/app\/domains\/shared\/util-common\/wait-for.ts\">waitFor<\/a> bridges the gap between the Signal world and the Promise world of the loader. It observes the given Signal with an <code>effect<\/code> and resolves a Promise as soon as the expected value arrives. I'll skip reproducing it here; the linked original is short.<\/p>\n<p>This solution works, but I'm still not entirely happy with it. The store already loads reactively as soon as its id changes. The resource on top exists only so that the router has something to wait for. That's a second wrapper around a mechanism that is already complete on its own.<\/p>\n<p>The wrapper can be avoided by turning things around: instead of building a resource around the store, the store hands out its resource itself. A small Signal Store is enough for that. It creates the resource in <code>withProps<\/code> and provides it in two flavors: the writable one internally, the read-only one to the outside:<\/p>\n<pre><code class=\"language-typescript\">\/\/ ...\/feature-booking\/passenger-edit\/passenger-store.ts\n\nexport const PassengerStore = signalStore(\n  { providedIn: &#039;root&#039; },\n\n  withState({\n    passengerId: 0,\n  }),\n\n  withProps((store) =&gt; {\n    const _passenger = inject(PassengerClient).findPassengerResourceById(\n      store.passengerId,\n      { withDefaultValue: false },\n    );\n\n    return {\n      _passenger,\n      passenger: _passenger.asReadonly(),\n    };\n  }),\n\n  withMethods((store) =&gt; ({\n    \/\/ Accepts a value or a signal\n    load: signalMethod&lt;number&gt;((id) =&gt; patchState(store, { passengerId: id })),\n  })),\n);<\/code><\/pre>\n<p>The underscore in <code>_passenger<\/code> is more than a convention: NgRx Signals treats members named this way as private and removes them from the store's public type. Only <code>passenger<\/code> is visible from the outside, the read-only view of the same resource provided by <code>asReadonly<\/code>. It can only be changed through <code>load<\/code>. This method is based on <code>signalMethod<\/code> and accepts either a value or a Signal; in the latter case, it keeps the id reactively up to date. The factory function for the route passes the id Signal to <code>load<\/code> and returns the store's resource:<\/p>\n<pre><code class=\"language-typescript\">\/\/ ...\/feature-booking\/passenger-edit\/shared-passenger-resource.ts\n\nexport function createSharedPassengerResource(params: Signal&lt;Params&gt;) {\n  const store = inject(PassengerStore);\n  const id = computed(() =&gt; Number(params()[&#039;id&#039;] ?? 0));\n  store.load(id);\n  return store.passenger;\n}<\/code><\/pre>\n<p>Store and router now share the same resource. The router waits for it and binds its value to the component, parameter changes with a reused component still reach the store via the Signal, and anyone who needs the value or loading state outside the route reads it directly from the store. Since the resource does without a default value, it blocks as intended.<\/p>\n<h2>Router Resources and Stores: An Assessment and an Alternative<\/h2>\n<p>Above all, these considerations show one thing: patterns for combining Router Resources and stores still have to establish themselves. Each of the variants shown comes at a price. The bridge via waitFor wraps the store a second time, the shared store has to be built specifically for this purpose and hand out its resource, and the guard forgoes blocking. Angular and NgRx may also still need to take a step toward each other here.<\/p>\n<p>If you don't want to block anyway, you need neither of them. A guard that kicks off the store synchronously and returns <code>true<\/code> right away is enough:<\/p>\n<pre><code class=\"language-typescript\">export const passengerGuard: CanActivateFn = (route) =&gt; {\n  const store = inject(PassengerDetailStore);\n  store.setPassengerId(Number(route.paramMap.get(&#039;id&#039;) ?? 0));\n  return true;\n};<\/code><\/pre>\n<p>The navigation goes through without delay, the store loads in the background, and the component takes value and loading state directly from the store. This corresponds to the non-blocking behavior from above but gets by without any additional resource. The two blocking variants thus remain reserved for the cases where the router really should wait for store data.<\/p>\n<h2>Learn More: Angular Architecture Workshop (Remote, Interactive, Advanced)<\/h2>\n<p>Become an expert in enterprise-wide and long-lived Angular applications with our Angular Architecture Workshop!<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/sujet-en.jpg\" alt=\"Angular Architecture Workshop\" style=\"width:600px; max-width:100%; display: block; margin: 0 auto 0 0\" \/><\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/en\/training\/advanced-angular-architecture-workshop\/\">German Version<\/a> | <a href=\"https:\/\/www.angulararchitects.io\/en\/training\/advanced-angular-architecture-workshop\/\">English Version<\/a><\/p>\n<h2>Summary<\/h2>\n<p>Router Resources bring the loading of route data into the Signal world. A route declares its data as ordinary resources, the router starts them all in parallel and binds the results to the component as inputs. The old resolver waterfall is gone; in the example, the waiting time shrank from 5 to 3 seconds.<\/p>\n<p>Blocking resources behave like resolvers with a better interface, non-blocking ones deliver the loading lifecycle directly to the target page. Via the <code>ActivatedRoute<\/code>, a resource can be reloaded without a new navigation, and a <code>RedirectCommand<\/code> in the loader redirects when data is missing. The interplay with Signal Stores deserves a second look: if you need to block, let the store hand out its resource itself or bridge it with a resource of your own; if you don't, simply kick it off via a guard. All of this is still experimental, but the direction is right.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Signal-based resolvers: parallel instead of waterfall, blocking or non-blocking Resolvers are as old as the Angular Router itself. They load data before a route is activated and return an Observable or a Promise for it. In a world where data access increasingly goes through resource, rxResource, and httpResource, that feels out of place. With version [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":34905,"comment_status":"open","ping_status":"open","sticky":true,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-34907","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Router Resources: Loading Data with the Angular Router - ANGULARarchitects<\/title>\n<meta name=\"description\" content=\"Router Resources in Angular 22: load data with resources, not resolvers \u2013 parallel instead of waterfall, blocking or not, with demo and Signal Stores.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Router Resources: Loading Data with the Angular Router - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Router Resources in Angular 22: load data with resources, not resolvers \u2013 parallel instead of waterfall, blocking or not, with demo and Signal Stores.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-08T17:45:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-09T07:49:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/social-sujet.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2400\" \/>\n\t<meta property=\"og:image:height\" content=\"1260\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Manfred Steyer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/social-sujet.png\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"TechArticle\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/\"},\"author\":{\"name\":\"Manfred Steyer\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\"},\"headline\":\"Router Resources: Loading Data with the Angular Router\",\"datePublished\":\"2026-09-08T17:45:38+00:00\",\"dateModified\":\"2026-09-09T07:49:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/\"},\"wordCount\":1859,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/\",\"name\":\"Router Resources: Loading Data with the Angular Router - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png\",\"datePublished\":\"2026-09-08T17:45:38+00:00\",\"dateModified\":\"2026-09-09T07:49:07+00:00\",\"description\":\"Router Resources in Angular 22: load data with resources, not resolvers \u2013 parallel instead of waterfall, blocking or not, with demo and Signal Stores.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png\",\"width\":1920,\"height\":1080},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Router Resources: Loading Data with the Angular Router\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\",\"name\":\"Manfred Steyer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Router Resources: Loading Data with the Angular Router - ANGULARarchitects","description":"Router Resources in Angular 22: load data with resources, not resolvers \u2013 parallel instead of waterfall, blocking or not, with demo and Signal Stores.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/","og_locale":"en_US","og_type":"article","og_title":"Router Resources: Loading Data with the Angular Router - ANGULARarchitects","og_description":"Router Resources in Angular 22: load data with resources, not resolvers \u2013 parallel instead of waterfall, blocking or not, with demo and Signal Stores.","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/","og_site_name":"ANGULARarchitects","article_published_time":"2026-09-08T17:45:38+00:00","article_modified_time":"2026-09-09T07:49:07+00:00","og_image":[{"width":2400,"height":1260,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/social-sujet.png","type":"image\/png"}],"author":"Manfred Steyer","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/social-sujet.png","twitter_misc":{"Written by":"Manfred Steyer","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"TechArticle","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/"},"author":{"name":"Manfred Steyer","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a"},"headline":"Router Resources: Loading Data with the Angular Router","datePublished":"2026-09-08T17:45:38+00:00","dateModified":"2026-09-09T07:49:07+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/"},"wordCount":1859,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/","name":"Router Resources: Loading Data with the Angular Router - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png","datePublished":"2026-09-08T17:45:38+00:00","dateModified":"2026-09-09T07:49:07+00:00","description":"Router Resources in Angular 22: load data with resources, not resolvers \u2013 parallel instead of waterfall, blocking or not, with demo and Signal Stores.","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/09\/hero.png","width":1920,"height":1080},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/router-resources-loading-data-with-the-angular-router\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Router Resources: Loading Data with the Angular Router"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a","name":"Manfred Steyer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","caption":"Manfred Steyer"}}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/34907","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=34907"}],"version-history":[{"count":5,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/34907\/revisions"}],"predecessor-version":[{"id":34919,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/34907\/revisions\/34919"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/34905"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=34907"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=34907"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=34907"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}