URL matching and configuration in AngularTS router
The AngularTS router matches the browser’s URL against registered state declarations and activates the best matching state. URL matching is an optional layer on top of the state machine—states can be navigated to programmatically without any URL involvement—but most applications use URLs to make deep-linking and browser history work correctly.
How URL matching works
When the browser URL changes (or on initial load), the UrlService calls sync(). It iterates all registered URL rules in priority order and finds the best match using a weighted scoring system. The winning rule’s handler is called, which calls $state.go() with the matched state and extracted parameter values.
URL rules are created automatically for every state that has a url property. You can also register custom URL rules directly on $urlService._rules.
UrlService.match(url) accepts a UrlParts object ({ path, search, hash }) and returns a MatchResult with the matching rule, the match data, and the match weight. Rules with the same sort order are ranked by weight so the most specific match wins.
URL parameters
Parameters are declared in the state’s url string. The router uses a UrlMatcher compiled from this pattern to test URLs and extract values.
Path parameters
Use a colon prefix for named path segments:
name: 'user',
url: '/users/:userId',
component: 'UserProfile'
});
// Matches: /users/42 → { userId: '42' }
Use curly braces for parameters with inline type annotations or custom regexps:
name: 'product',
url: '/products/{productId:int}',
component: 'ProductDetail'
});
// Matches: /products/123 → { productId: 123 } (integer, not string)
// Does NOT match: /products/abc
Custom regexp:
name: 'article',
url: '/articles/{slug:[a-z0-9-]+}',
component: 'Article'
});
Query parameters
Append a ? followed by parameter names. Multiple query params are separated by &:
name: 'search',
url: '/search?q&page',
component: 'SearchResults'
});
// Matches: /search?q=angularts&page=2 → { q: 'angularts', page: '2' }
Typed query params:
name: 'messages',
url: '/messages?{before:date}&{after:date}',
component: 'MessageList'
});
Mixed path and query params:
name: 'mailbox',
url: '/messages/:mailboxId?{before:date}&{after:date}',
component: 'Mailbox'
});
Optional parameters
Give a parameter a default value in the params block and set squash: true to make it optional. When the URL is visited without the parameter, the default value is used. When navigating with the default value, the parameter is omitted from the URL:
name: 'userList',
url: '/users/:page',
params: {
page: {
value: '1', // default value
squash: true // remove from URL when value equals default
}
},
component: 'UserList'
});
// /users/ → { page: '1' } (squashed)
// /users/3 → { page: '3' }
Built-in parameter types
The UrlConfigProvider ships the following built-in parameter types. Specify the type inline in the URL pattern or in the params block:
string
Default for path parameters. Encodes and decodes as a plain string. Slashes within the value are encoded as ~2F in AngularTS’s patched path type to avoid ambiguity in Angular 1’s $location.
url: '/items/:name'
// { name: 'foo bar' } → /items/foo%20bar
int
Parses URL segments as integers using parseInt. The pattern is /\d+/.
url: '/users/{id:int}'
// /users/42 → { id: 42 } (number, not string)
bool
Represents boolean values. Encodes true as "1" and false as "0". The pattern matches 1 or 0.
url: '/settings/{darkMode:bool}'
// /settings/1 → { darkMode: true }
json
Encodes arbitrary objects as a JSON string in the URL (URL-encoded). Useful for complex filter objects.
url: '/search?{filters:json}'
// { filters: { status: 'active', role: 'admin' } }
// → /search?filters=%7B%22status%22%3A%22active%22%7D
date
Encodes Date objects as YYYY-MM-DD strings. Parses the string back to a Date on the way in.
url: '/events?{startDate:date}&{endDate:date}'
// { startDate: new Date('2024-01-15') }
// → /events?startDate=2024-01-15
hash
The internal parameter type used for the # (hash/anchor) portion of the URL. Has inherit: false so the hash is not carried forward to child state transitions.
URL configuration with UrlConfigProvider
UrlConfigProvider (injected as $urlConfigProvider in config blocks, or accessed via $url._config at runtime) controls global URL matching behavior.
Case sensitivity
URL matching is case-sensitive by default. Allow case-insensitive matching:
.config(function ($urlConfigProvider) {
$urlConfigProvider.caseInsensitive(true);
});
Strict mode (trailing slashes)
By default, /users/ and /users are distinct. Disable strict mode to treat trailing slashes as equivalent:
.config(function ($urlConfigProvider) {
$urlConfigProvider.strictMode(false); // /users/ matches /users
});
Default squash policy
Control the global default for how parameters with default values appear in URLs:
.config(function ($urlConfigProvider) {
// 'false' (default): include the default value in the URL
// 'true': omit default value from URL
// '~': replace default value with '~' in the URL
$urlConfigProvider.defaultSquashPolicy(true);
});
Custom parameter types
Register a custom ParamType before using it in state URL patterns. The type must implement encode, decode, is, equals, and optionally pattern:
.config(function ($urlConfigProvider) {
// Encodes an array of integers as a dash-separated string
$urlConfigProvider.type('intarray', {
encode: function (array) {
return array.join('-');
},
decode: function (str) {
return str.split('-').map(function (x) { return parseInt(x, 10); });
},
is: function (val) {
return Array.isArray(val) && val.every(function (x) {
return typeof x === 'number' && !isNaN(x);
});
},
equals: function (a, b) {
return a.length === b.length &&
a.every(function (x, i) { return x === b[i]; });
},
pattern: /[0-9]+(?:-[0-9]+)*/
});
});
Use the custom type in a state URL:
name: 'report',
url: '/reports/{ids:intarray}',
component: 'Report'
});
// $state.go('report', { ids: [10, 20, 30] }) → /reports/10-20-30
// /reports/10-20-30 → { ids: [10, 20, 30] }
Note: Register custom types before any state that uses them.
UrlConfigProvider.type()returns the provider itself for chaining.
Hash mode vs HTML5 mode
AngularTS inherits Angular 1’s $location modes. Configure the mode on $locationProvider:
Hash mode (default)
URLs use a hash fragment: http://example.com/app/#/contacts/42. No server configuration is needed; the hash segment is never sent to the server.
angular.module('app')
.config(function ($locationProvider) {
$locationProvider.html5Mode(false);
$locationProvider.hashPrefix('!'); // optional: use #!/ instead of #/
});
The UrlService.href() method prepends # (plus the hash prefix) when generating hrefs in hash mode.
HTML5 mode (pushState)
URLs use real paths: http://example.com/contacts/42. The server must return the app’s index.html for all routes.
angular.module('app')
.config(function ($locationProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: true // <base href="..."> must be present in <head>
});
});
In HTML5 mode, UrlService.href() prepends the base path (stripped of its last segment) rather than a hash.
Base href
In HTML5 mode, the base href is read from the <base> tag in the document <head>:
<base href="/myapp/">
</head>
UrlService.baseHref() returns the current base href. If no <base> tag is present it falls back to window.location.pathname. The base href is used when constructing absolute URLs via $state.href(..., { absolute: true }) and when pushing new history entries.
var base = $url.baseHref(); // "/myapp/"
Reading the current URL
UrlService provides three methods to read URL components:
$url.getPath(); // "/contacts/42"
$url.getSearch(); // { tab: 'notes' }
$url.getHash(); // "section1"
// All three at once
var parts = $url.parts();
// { path: '/contacts/42', search: { tab: 'notes' }, hash: 'section1' }
// The full normalized URL (strips base, adds hash prefix in hash mode)
$url.url(); // "/contacts/42?tab=notes#section1"
Updating the URL
UrlService.url(newUrl) replaces the current URL. The router then calls sync() to find and activate the matching state:
$url.url('/contacts/99?tab=history');
UrlService.push() is the internal method used by the built-in onSuccess hook to update the browser address bar after a successful state transition:
$url.push(state.navigable.url, $state.params, { replace: false });
URL rule priority and matching weight
When multiple rules could match the same URL, the router scores each match and picks the winner:
- Rules are sorted by a primary sort order (rules created from state declarations all share the same group).
- Within a group, the match with the highest weight wins. Weight is computed by counting matched segments, typed parameters, and specificity of regexps.
- A state URL like
/users/{id:int}outweighs/users/:idfor the path/users/42because the typed parameter provides a stricter match.
Exact path matches score higher than prefix matches. Query parameters do not affect path scoring but must all be present if declared without defaults.
Listening for URL changes
UrlService.onChange(callback) registers a low-level listener that fires on every $locationChangeSuccess event. The listener receives the Angular scope event:
console.log('URL changed to:', $url.url());
});
// Stop listening
deregister();
UrlService.listen(false) stops the router from responding to URL changes entirely. Call listen(true) to resume. This is useful when loading states asynchronously and you want to defer URL-driven navigation until the states are registered:
.run(function ($url, $stateRegistry) {
$url.listen(false);
fetch('/api/states')
.then(r => r.json())
.then(function (states) {
states.forEach(s => $stateRegistry.register(s));
$url.listen(true);
$url.sync(); // activate the state matching the current URL
});
});
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.