Skip to main content

URLs and Parameters

UI-Router provides sophisticated URL routing with typed parameters, hierarchical composition, and flexible encoding/decoding.

URL Declaration

URLs are declared on states using the url property:
const state = {
  name: 'users',
  url: '/users',
  component: UsersComponent
};

Hierarchical URLs

Child state URLs are appended to parent URLs:
const states = [
  { name: 'app', url: '/app' },
  { name: 'app.users', url: '/users' },
  { name: 'app.users.detail', url: '/:userId' }
];

// Resulting URLs:
// app              -> /app
// app.users        -> /app/users
// app.users.detail -> /app/users/:userId
Child URLs starting with ^ are absolute and don’t append to parent:
{ name: 'modal', parent: 'app', url: '^/modal' }
// URL is /modal (not /app/modal)

URL Parameters

Path Parameters

Path parameters use :paramName syntax:
const state = {
  name: 'user',
  url: '/users/:userId',
  // Matches: /users/123, /users/abc
};

// Navigate
$state.go('user', { userId: 123 });
// URL: /users/123

Query Parameters

Query parameters use ?paramName syntax:
const state = {
  name: 'search',
  url: '/search?query&category&page',
  // Matches: /search?query=test&category=books&page=2
};

// Navigate
$state.go('search', { query: 'test', category: 'books', page: 2 });
// URL: /search?query=test&category=books&page=2

Mixed Parameters

const state = {
  name: 'products',
  url: '/products/:categoryId?sort&page',
  // Matches: /products/electronics?sort=price&page=1
};

Parameter Types

From ParamDeclaration:
export interface ParamDeclaration {
  /** The parameter's type */
  type?: string | ParamType;
  
  /** Default value */
  value?: any;
  
  /** Array mode */
  array?: boolean;
  
  /** Squash default values */
  squash?: boolean | string;
  
  /** Dynamic parameter */
  dynamic?: boolean;
  
  /** Raw (no URL encoding) */
  raw?: boolean;
  
  /** Inherit parameter value */
  inherit?: boolean;
}

Built-in Types

UI-Router includes several built-in parameter types:
Default type for path parameters.
url: '/users/:name'
// name is type 'string'

Custom Parameter Types

From ParamTypeDefinition:
export interface ParamTypeDefinition {
  /** Pattern to match in URL */
  pattern?: RegExp;
  
  /** Type check */
  is(val: any, key?: string): boolean;
  
  /** Encode value to string */
  encode(val: any, key?: string): string | string[];
  
  /** Decode string to value */
  decode(val: string, key?: string): any;
  
  /** Check equality */
  equals(a: any, b: any): boolean;
  
  /** Configuration */
  raw?: boolean;
  dynamic?: boolean;
  inherit?: boolean;
}

Example: Custom Type

// Register custom type
router.urlService.config.type('csv', {
  // Encode array as CSV string
  encode: (array) => array ? array.join(',') : '',
  
  // Decode CSV string to array
  decode: (str) => str ? str.split(',') : [],
  
  // Pattern matches comma-separated values
  pattern: /^[^,]+(,[^,]+)*$/,
  
  // Check if value is array
  is: (val) => Array.isArray(val),
  
  // Check array equality
  equals: (a, b) => a && b && a.length === b.length && 
    a.every((val, i) => val === b[i])
});

// Use custom type
const state = {
  name: 'tags',
  url: '/tags/{tags:csv}',
};

$state.go('tags', { tags: ['javascript', 'typescript', 'angular'] });
// URL: /tags/javascript,typescript,angular

Parameter Configuration

Default Values

const state = {
  name: 'list',
  url: '/list?page&size',
  params: {
    page: { value: 1, type: 'int' },
    size: { value: 20, type: 'int' }
  }
};

// Navigate without params
$state.go('list');
// Uses defaults: { page: 1, size: 20 }

Shorthand Syntax

// Full syntax
params: {
  page: { value: 1 }
}

// Shorthand
params: {
  page: 1
}

Array Parameters

From ParamDeclaration.array:
const state = {
  name: 'search',
  url: '/search?{tags:int}',
  params: {
    tags: { 
      array: true,
      value: []
    }
  }
};

$state.go('search', { tags: [1, 2, 3] });
// URL: /search?tags=1&tags=2&tags=3
For query parameters, array: 'auto' allows single or multiple values:
params: {
  tags: { array: 'auto' }
}

// Single value: /search?tags=1  -> tags = '1'
// Multiple: /search?tags=1&tags=2  -> tags = ['1', '2']

Squash Parameters

From ParamDeclaration.squash: Control how default values appear in URLs:
const state = {
  name: 'list',
  url: '/list/:category',
  params: {
    category: { 
      value: 'all',
      squash: true  // Omit default from URL
    }
  }
};

$state.go('list', { category: 'all' });
// URL: /list (category omitted)

$state.go('list', { category: 'books' });
// URL: /list/books
With custom squash string:
params: {
  category: {
    value: 'all',
    squash: '~'  // Replace default with '~'
  }
}

$state.go('list', { category: 'all' });
// URL: /list/~

Dynamic Parameters

From ParamDeclaration.dynamic: Dynamic parameters don’t trigger state reload:
const state = {
  name: 'search',
  url: '/search?query&page',
  params: {
    query: { dynamic: true },
    page: { dynamic: true, value: 1 }
  },
  component: SearchComponent
};

// Component doesn't reload
$state.go('search', { query: 'new search' });
// Only parameters change, component stays mounted

Raw Parameters

From ParamDeclaration.raw: Disable URL encoding:
const state = {
  name: 'product',
  url: '/product/:slug',
  params: {
    slug: { 
      type: 'string',
      raw: true  // Don't encode slashes
    }
  }
};

$state.go('product', { slug: 'electronics/phones/iphone' });
// URL: /product/electronics/phones/iphone
// (not /product/electronics%2Fphones%2Fiphone)
Be careful with raw parameters as they can create ambiguous URLs.

Non-URL Parameters

Parameters can exist without being in the URL:
const state = {
  name: 'detail',
  url: '/detail/:id',
  params: {
    id: null,           // URL parameter
    returnTo: null,     // Non-URL parameter
    openModal: false    // Non-URL parameter
  }
};

$state.go('detail', { 
  id: 123,
  returnTo: 'list',
  openModal: true 
});
// URL: /detail/123
// returnTo and openModal are in params but not URL

URL Service

The UrlService manages URL synchronization:

URL Synchronization

// Start listening to URL changes
router.urlService.listen();

// Trigger initial URL sync
router.urlService.sync();

// Defer URL interception during setup
router.urlService.deferIntercept();
// ... register states ...
router.urlService.listen();

Generate URLs

// Generate URL for state
const url = $state.href('users.detail', { userId: 123 });
// Returns: '/users/123'

// Absolute URL
const absUrl = $state.href('users', {}, { absolute: true });
// Returns: 'http://example.com/users'

URL Rules

URL rules provide advanced routing:
// When rule
router.urlService.rules.when('/old-url', '/new-url');

// Otherwise (fallback)
router.urlService.rules.otherwise('/home');

// Initial rule (runs once)
router.urlService.rules.initial({ state: 'home' });

// Custom rule
router.urlService.rules.rule({
  match: (url) => {
    return url.path === '/special';
  },
  handler: (match, url, router) => {
    return router.stateService.target('special');
  }
});

URL Matching

From UrlMatcher: URLs are matched using compiled patterns:
const state = {
  name: 'users',
  url: '/users/{userId:int}?{page:int}&search',
};

// The URL is compiled to a UrlMatcher
// Matches: /users/123?page=1&search=john
// Extracts: { userId: 123, page: 1, search: 'john' }

Pattern Matching

// Exact match
url: '/users'

// Parameter
url: '/users/:id'

// Typed parameter
url: '/users/{id:int}'

// Optional parameter (with ?)
url: '/users/:id?'

// Regex constraint
url: '/users/{id:[0-9]{1,8}}'

// Query parameters
url: '/search?query&category'

// Typed query parameters
url: '/search?{query}&{page:int}'

Parameter Inheritance

Parameters are inherited during transitions:
const states = [
  {
    name: 'users',
    url: '/users?sort',
    params: { sort: 'name' }
  },
  {
    name: 'users.detail',
    url: '/:userId'
  }
];

// Navigate to users
$state.go('users', { sort: 'date' });

// Navigate to detail (inherits sort)
$state.go('users.detail', { userId: 123 });
// Parameters: { sort: 'date', userId: 123 }
Disable inheritance:
$state.go('users.detail', { userId: 456 }, { inherit: false });
// Parameters: { userId: 456 } (sort not inherited)
Or at the parameter level:
params: {
  tempFlag: { 
    value: false,
    inherit: false  // Never inherited
  }
}

Best Practices

// Good: Type ensures validation
url: '/users/{userId:int}'

// Avoid: No type validation
url: '/users/:userId'
params: {
  sortBy: { dynamic: true },
  page: { dynamic: true },
  selectedTab: { dynamic: true }
}
// These changes don't reload the component
params: {
  page: { value: 1, type: 'int' },
  size: { value: 20, type: 'int' }
}
url: '/list/:filter',
params: {
  filter: { value: 'all', squash: true }
}
// /list instead of /list/all
// Good
url: '/products/:category/:productId'

// Avoid
url: '/p?c&id'

Next Steps

States

Learn about state declarations

Transitions

Navigate with parameters

State Tree

Understand URL composition

Views

Use parameters in views

Build docs developers (and LLMs) love