# ngx-quicklink prefetches the Angular routes a reader can actually see

> ngx-quicklink is an Angular router preloading strategy that uses Intersection Observer, requestIdleCallback and the network information API to download the lazy modules behind visible links, and it weighs about 2KB minified and gzipped.

**mgechev/ngx-quicklink** — Quicklink prefetching strategy for the Angular router

- Repository: https://github.com/mgechev/ngx-quicklink
- Stars: 755 · Forks: 43
- Language: TypeScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/mgechev-ngx-quicklink

## A preloading strategy that reads the viewport

ngx-quicklink is an implementation of the quicklink idea for the Angular router, and it provides a router preloading strategy that automatically downloads the lazy-loaded modules associated with all the visible links on the screen. The distinction from stock Angular preloading is worth stating plainly, because Angular already has a PreloadAllModules strategy. That one eagerly fetches every lazy chunk in the application as soon as the app is idle, whether or not the user will ever click through to it. This one looks at the screen. The project describes itself as a drop-in solution for sites that want to prefetch links based on what is in the user's viewport, and it sets a size target of about 2KB minified and gzipped. The audience is therefore an Angular team that has already split its application into lazy routes and wants the network work to follow reading intent rather than precede it. The cost of that choice is bandwidth, and the framework handles it by being conservative about when and whether to prefetch at all.

## Four signals decide whether a prefetch happens

The mechanism is a sequence of checks, and each one can veto the download. First the library detects routerLinks that fall within the viewport, using the Intersection Observer API, so a link the reader cannot see is not a candidate. Second it waits until the browser is idle, using requestIdleCallback, so the prefetch does not compete with the rendering the reader is waiting for. Third it checks the connection. The documentation names two specific signals, navigator.connection.effectiveType to detect a slow connection and navigator.connection.saveData to detect that the user has data saver enabled. This third check is the one that keeps the feature honest on a metered or throttled connection, and it is why the library can be a drop-in without becoming a bandwidth problem. Fourth, and only then, it prefetches the lazy-loaded modules using Angular's own prefetching strategy, so the module ends up in the same cache the router uses and a subsequent navigation is a cache hit rather than a network request. Note that the browser APIs involved are the ones that vary across browsers, which the polyfill section addresses directly.

## Wiring the strategy into a module based application

The package is installed from npm:

```bash
npm i ngx-quicklink --save
```

After that you import the QuicklinkModule into the AppModule and pass QuicklinkStrategy as the preloadingStrategy in the router configuration:

```ts
import { QuicklinkModule, QuicklinkStrategy } from 'ngx-quicklink';

@NgModule({
  declarations: [...],
  imports: [
    // ...
    QuicklinkModule,
    RouterModule.forRoot(routes, { preloadingStrategy: QuicklinkStrategy }),
  ],
  bootstrap: [...]
})
export class AppModule {}
```

Individual routes can opt out through the data property, which is how you keep a page that is expensive to load off the prefetch path:

```ts
export const routes: Routes = [
  {
    path: 'contact',
    loadChildren: import(() => './contact/contact.module').then(m => m.ContactModule),
    data: {
      preload: false
    }
  }
];
```

There is a scoping rule that catches people out. To make the module available inside lazy-loaded modules as well, you need to import it in a shared module and export it, and the documentation links a commit in an example application that shows the integration in the angular-realworld-example-app. The reasoning is structural. A lazy module has its own injector, so a directive declared in the eagerly loaded root module does not reach into it, and without the shared module trick the links inside lazy routes are never observed.

## Standalone components need the directive instead of the module

The standalone API is a different shape and uses different exports. Instead of a module you register a providers array and the standalone router helper:

```ts
import { QuicklinkStrategy, quicklinkProviders } from 'ngx-quicklink';

export const appConfig: ApplicationConfig = {
  providers: [
    quicklinkProviders,
    provideRouter(
      routes,
      withPreloading(QuicklinkStrategy)
    ),
  ],
};
```

Then the QuicklinkDirective is imported into each standalone component that has router links, alongside RouterLink:

```ts
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
import { QuicklinkDirective } from 'ngx-quicklink';

@Component({
  standalone: true,
  imports: [RouterLink, QuicklinkDirective],
  template: `
    <a routerLink="/about">About</a>
  `,
})
export default class HomeComponent {
}
```

The per-component import is the part that scales badly in a large application. A module based app can import QuicklinkModule once in the root and inherit it, while a standalone app repeats the directive import in every component that renders a link, and a component that forgets it simply does not participate in prefetching. That is a real maintenance cost of the standalone model rather than a defect in the library, but it changes how much attention the integration needs in a codebase with many small components.

## If nothing is preloaded, look for the ngx-ql attribute

The debugging section is short and unusually specific. If routes are not being preloaded, the documentation says the most likely cause is a missing import of the QuicklinkModule, because that module exports a LinkDirective which matches the routerLink selector and hooks into all the router links in the scope of the module, observing their visibility. If the module is not imported correctly, the directive is missing and the strategy has nothing to work with. The verification step is equally concrete. Inspect a router link and check whether it has the ngx-ql attribute. If it does not, the QuicklinkModule needs to be imported in the module that defines the compilation context of the template containing the router link, which is a more precise statement than importing it somewhere and hoping. If the attribute is present but the prefetching still does not behave as expected, the documentation asks you to open an issue. The older browser case is separate and documented under polyfills. IntersectionObserver is optional, and on browsers without it ngx-quicklink preloads all links on the page, which is a different behaviour rather than a broken one. The library includes a very small fallback for requestIdleCallback, and a conditional polyfill can be loaded for the older case.

## Observing the screen versus predicting the click

The README carries a FAQ that is worth quoting because it states the trade-off more honestly than a feature list would. The first question is the difference between quicklink and ngx-quicklink. The answer is that quicklink prefetches the resource pointed at by the href attribute of an anchor, which does not work for Angular because the value of that attribute is not a JavaScript bundle but a path defined inside the routing configuration, so ngx-quicklink adds the extra functionality needed to make the same strategy work with Angular. The second question asks whether to use Guess.js or ngx-quicklink, and the answer is that the prefetching behaviour of Guess.js would most likely be more accurate than ngx-quicklink, which would reduce the overfetching, but Guess.js may take a little more effort to set up, and if you do not want to integrate the analytics of your website then ngx-quicklink is the right choice. That is the whole trade in two sentences. Predictive prefetching costs analytics integration and a learning phase, while observational prefetching costs some overfetching and needs no telemetry at all. For a team that does not want to ship behavioural analytics, the second option is the only one on the table.

## Conclusion

Adopt ngx-quicklink if your Angular application already lazy-loads its routes and you want the prefetch to follow what the reader can see rather than a guess about what they will click. Do not adopt it to fix slow first loads on a page that has no lazy routes, since there is nothing for it to prefetch, and do not expect it to remove overfetching, because the README is explicit that a predictive approach can be more accurate. Before you wire it in, check that the routes you care about are lazy loaded, confirm the ngx-ql attribute appears on your links after setup, and verify on a slow connection that the effectiveType and saveData checks are actually skipping the prefetch.

## FAQ

### What does ngx-quicklink do in an Angular application?

It provides a router preloading strategy that automatically downloads the lazy-loaded modules associated with all the visible links on the screen. It detects routerLinks in the viewport with Intersection Observer, waits for browser idle with requestIdleCallback, skips slow or data saver connections, and then prefetches through Angular's own prefetching strategy.

### How do I install and enable ngx-quicklink?

Install the package with npm i ngx-quicklink --save, then import QuicklinkModule into your AppModule and pass QuicklinkStrategy as the preloadingStrategy in RouterModule.forRoot. In a standalone application, use quicklinkProviders together with withPreloading(QuicklinkStrategy) and import QuicklinkDirective into each standalone component that renders router links.

### How do I stop a route from being preloaded?

Set the data property on the route with preload: false, for example data: { preload: false } next to loadChildren. The README also notes that to make the module available inside lazy-loaded modules you have to import it in a shared module and export it.

### Why are my routes not being preloaded?

The most likely cause is a missing import of the QuicklinkModule, because that module exports the LinkDirective that matches the routerLink selector and observes its visibility. To check, inspect a router link and see whether it carries the ngx-ql attribute, and if it does not, import QuicklinkModule in the module that defines the compilation context of the template.

### When should I use Guess.js instead of ngx-quicklink?

The README states that Guess.js prefetching would most likely be more accurate and would reduce overfetching, but it may take more effort to set up. If you do not want to integrate your website's analytics, the README considers ngx-quicklink the right choice.

## Sources

- [Issues](https://github.com/mgechev/ngx-quicklink/issues)
- [License: MIT](https://github.com/mgechev/ngx-quicklink/blob/master/LICENSE)
- [mgechev/ngx-quicklink on GitHub](https://github.com/mgechev/ngx-quicklink)
- [README](https://github.com/mgechev/ngx-quicklink/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mgechev-ngx-quicklink
