npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dasch-ng/route-signals

v1.0.0-rc.0

Published

[![npm version](https://img.shields.io/npm/v/@dasch-ng/route-signals.svg)](https://www.npmjs.com/package/@dasch-ng/route-signals)

Readme

@dasch-ng/route-signals

npm version

Angular utilities for working with route parameters, query parameters, and route data as signals.

Features

  • 🎯 Type-safe signals - Get route state with full TypeScript support
  • 🔄 Automatic updates - Signals update when route changes
  • 🔐 URL decoding - Automatically decodes URL-encoded values
  • 🛡️ Error handling - Safe decoding with fallback for malformed URLs
  • Zero dependencies - Only depends on Angular and RxJS
  • 📦 Tree-shakeable - Import only what you need

Installation

npm install @dasch-ng/route-signals

Usage

Route Parameters

Track URL path parameters as signals:

import { Component } from '@angular/core';
import { routeParam } from '@dasch-ng/route-signals';

@Component({
  selector: 'app-user-detail',
  template: `
    <h1>User: {{ userId() }}</h1>
    <p>Group: {{ groupId() }}</p>
  `,
})
export class UserDetailComponent {
  // Route: /users/:userId/groups/:groupId
  readonly userId = routeParam('userId');
  readonly groupId = routeParam('groupId');

  constructor() {
    // URL: /users/john-doe/groups/admin
    console.log(this.userId()); // "john-doe"
    console.log(this.groupId()); // "admin"

    // Automatically decodes URL-encoded values
    // URL: /users/john%20doe/groups/team%20alpha
    console.log(this.userId()); // "john doe"
    console.log(this.groupId()); // "team alpha"
  }
}

Query Parameters

Track URL query parameters as signals:

import { Component } from '@angular/core';
import { routeQueryParam } from '@dasch-ng/route-signals';

@Component({
  selector: 'app-search',
  template: `
    <h1>Search: {{ query() }}</h1>
    <p>Filter: {{ filter() }}</p>
    <p>Sort: {{ sort() }}</p>
  `,
})
export class SearchComponent {
  // URL: /search?q=angular&filter=tutorials&sort=recent
  readonly query = routeQueryParam('q');
  readonly filter = routeQueryParam('filter');
  readonly sort = routeQueryParam('sort');

  constructor() {
    console.log(this.query()); // "angular"
    console.log(this.filter()); // "tutorials"
    console.log(this.sort()); // "recent"

    // Automatically decodes URL-encoded values
    // URL: /search?q=angular%20signals&filter=a%2Fb
    console.log(this.query()); // "angular signals"
    console.log(this.filter()); // "a/b"
  }
}

Route Data

Track route data properties as signals:

import { Component } from '@angular/core';
import { routeData } from '@dasch-ng/route-signals';

// In route configuration:
const routes = [
  {
    path: 'admin',
    component: AdminComponent,
    data: { title: 'Admin Dashboard', role: 'admin' },
  },
];

@Component({
  selector: 'app-admin',
  template: `
    <h1>{{ title() }}</h1>
    <p>Required role: {{ role() }}</p>
  `,
})
export class AdminComponent {
  readonly title = routeData<string>('title');
  readonly role = routeData<string>('role');

  constructor() {
    console.log(this.title()); // "Admin Dashboard"
    console.log(this.role()); // "admin"
  }
}

With Resolvers

Route data works seamlessly with Angular resolvers:

import { inject } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router';
import { UserService } from './user.service';

// Resolver
const userResolver = (route: ActivatedRouteSnapshot) => {
  const userService = inject(UserService);
  const userId = route.paramMap.get('id');
  return userService.getUser(userId);
};

// Route configuration
const routes = [
  {
    path: 'user/:id',
    component: UserDetailComponent,
    resolve: { user: userResolver },
    // Re-run resolver when params change
    runGuardsAndResolvers: 'paramsChange',
  },
];

// Component
@Component({
  selector: 'app-user-detail',
  template: `
    <h1>{{ user().name }}</h1>
    <p>{{ user().email }}</p>
  `,
})
export class UserDetailComponent {
  readonly user = routeData<User>('user');

  // Signal automatically updates when resolver re-runs
}

API

routeParam(key: string): Signal<string>

Creates a signal that tracks a URL-encoded route parameter.

  • Parameters:
    • key: The name of the route parameter to track
  • Returns: A signal containing the decoded parameter value
  • Throws: Error if the parameter is not present in the current route

routeQueryParam(key: string): Signal<string>

Creates a signal that tracks a URL-encoded query parameter.

  • Parameters:
    • key: The name of the query parameter to track
  • Returns: A signal containing the decoded query parameter value
  • Throws: Error if the query parameter is not present in the current route

routeData<T>(key: string): Signal<T>

Creates a signal that tracks a route data property.

  • Type Parameters:
    • T: The type of the data property
  • Parameters:
    • key: The name of the data property to track
  • Returns: A signal containing the route data value
  • Throws: Error if the data property is not present in the current route

URL Decoding

All functions automatically decode URL-encoded values using a safe decoder that:

  • Decodes standard URL encoding (e.g., %20 → space)
  • Handles special characters (e.g., %2F/, %3D=)
  • Decodes Unicode characters (e.g., %E2%9C%93)
  • Safely handles malformed encoding (logs warning and returns original string)

Error Handling

All functions throw an error if the requested parameter/data is not present in the route. This helps catch configuration errors early:

// If route doesn't have an 'id' parameter
const userId = routeParam('id'); // ❌ Throws: "id is not in route."

// If route doesn't have a 'search' query parameter
const search = routeQueryParam('search'); // ❌ Throws: "Query parameter "search" is not in route."

// If route data doesn't have a 'title' property
const title = routeData('title'); // ❌ Throws: "Route data property "title" is not in route."

Requirements

  • Angular 19.0.0 or higher
  • RxJS 7.5.0 or higher

License

MIT © Daniel Schuba

Links