Overview
The StateDeclaration interface defines the configuration object for declaring states in UI-Router. Each state in your application is defined using this interface.
Core Properties
name
The state name (required for registered states).
Behavior:
- Must be unique across all registered states
- Use dot notation for parent/child relationships:
'parent.child'
- Acts as the state’s identifier
Example:
{ name: 'home' }
{ name: 'about' }
{ name: 'users.list' }
{ name: 'users.detail' }
url
The URL fragment for the state.
Behavior:
- Appended to parent state’s URL to build full URL
- Supports parameter syntax:
:param, {param}, {param:type}
- Supports query parameters:
?query&another
- Can use
^ to break URL inheritance
Example - Basic URL:
{ name: 'home', url: '/home' }
Example - Path parameters:
// Parameter with default regexp
{ name: 'users', url: '/users/:userId' }
// Parameter with custom regexp
{ name: 'books', url: '/books/{bookId:[a-zA-Z_-]}' }
// Parameter with type
{ name: 'category', url: '/category/{categoryId:int}' }
// Multiple parameters
{ name: 'publisher', url: '/books/{publishername:string}/{categoryId:int}' }
Example - Query parameters:
// Basic query params
{ name: 'messages', url: '/messages?before&after' }
// Typed query params
{ name: 'messages', url: '/messages?{before:date}&{after:date}' }
// Combined path and query
{ name: 'mailbox', url: '/messages/:mailboxid?{before:date}&{after:date}' }
parent
Explicitly sets the parent state.
parent?: string | StateDeclaration
Behavior:
- Normally implied from the state’s name (e.g.,
'parent.child')
- Can be set explicitly to use shorter state names
- Accepts parent state name (string) or parent state object
Example:
const parentState = {
name: 'parentstate'
};
const childState = {
name: 'childstate',
parent: 'parentstate'
// Or: parent: parentState
};
// Creates 'parentstate.childstate' hierarchy
// Can reference as just 'childstate' in templates
abstract
Abstract state indicator.
Behavior:
- Abstract states cannot be directly activated
- Used to provide inherited properties to children
- Useful for layouts, common resolves, and base configurations
Example:
// Layout state with abstract
{
name: 'app',
abstract: true,
component: AppLayoutComponent
}
// Child states can be activated
{ name: 'app.home', url: '/home' }
{ name: 'app.about', url: '/about' }
URL Parameters
params
Parameter configuration object.
params?: { [key: string]: ParamDeclaration | any }
Behavior:
- Configures parameters declared in the URL
- Defines additional non-URL parameters
- Each key is a parameter name
- Value can be a ParamDeclaration or shorthand
Example - Configure URL params:
{
name: 'users',
url: '/users/:userId',
params: {
userId: {
type: 'int',
value: null
}
}
}
Example - Non-URL params:
{
name: 'messages',
url: '/messages',
params: {
// Non-URL parameter (not in URL)
filter: {
value: 'inbox',
squash: true
},
// Array parameter
tags: {
type: 'int',
array: true,
value: []
}
}
}
Example - Shorthand syntax:
{
name: 'search',
url: '/search?query',
params: {
query: 'default', // shorthand for { value: 'default' }
page: 1,
sort: 'name'
}
}
dynamic
Marks all state parameters as dynamic.
Behavior:
- When
true, all parameters use dynamic behavior by default
- Dynamic params can change without triggering state reload
- Individual parameters can override via
ParamDeclaration.dynamic
Example:
{
name: 'products',
url: '/products?page&sort&filter',
dynamic: true,
// Changing page/sort/filter won't reload the state
}
reloadOnSearch
Marks query parameters as dynamic.
Deprecated. Use dynamic or ParamDeclaration.dynamic instead.
Data and Resolves
data
Inherited property to store state metadata.
Behavior:
- Child states’
data prototypally inherit from parent state
- Good for storing metadata like
requiresAuth, pageTitle, etc.
- Changes to parent
data reflect in child data
Example:
// Parent state
{
name: 'admin',
data: {
requiresAuth: true,
role: 'admin'
}
}
// Child inherits data
{
name: 'admin.users',
data: {
pageTitle: 'User Management'
}
// Inherits requiresAuth and role from parent
}
Example - Using in hooks:
transitionService.onBefore({}, (trans) => {
const requiresAuth = trans.to().data?.requiresAuth;
if (requiresAuth && !isAuthenticated()) {
return trans.router.stateService.target('login');
}
});
resolve
Asynchronously fetch data during transition.
resolve?: ResolveTypes[] | { [key: string]: IInjectable }
Behavior:
- Fetches data asynchronously when entering the state
- Delays state activation until all resolves complete
- Data available in views, hooks, and child state resolves
- Retained until state is exited
Example - Array syntax (recommended):
{
name: 'user',
url: '/user/:userId',
resolve: [
{
token: 'user',
deps: ['UserService', Transition],
resolveFn: (userService, trans) => {
return userService.fetchUser(trans.params().userId);
}
}
]
}
Example - Object syntax (AngularJS style):
{
name: 'users',
resolve: {
users: ['UserService', (userService) => {
return userService.list();
}],
// Or unannotated
currentUser: (AuthService) => AuthService.getCurrentUser()
}
}
Example - Resolve dependencies:
{
name: 'userPosts',
resolve: [
// First resolve
{
token: 'user',
deps: ['UserService', Transition],
resolveFn: (userService, trans) =>
userService.get(trans.params().userId)
},
// Second resolve depends on first
{
token: 'posts',
deps: ['PostService', 'user'],
resolveFn: (postService, user) =>
postService.getByUser(user.id)
}
]
}
resolvePolicy
Sets resolve policy defaults.
resolvePolicy?: ResolvePolicy
ResolvePolicy interface:
interface ResolvePolicy {
when?: 'LAZY' | 'EAGER'; // When to fetch
async?: 'WAIT' | 'NOWAIT' | CustomAsyncPolicy; // Wait behavior
}
Values:
when: 'LAZY' - Fetch just before entering state (default)
when: 'EAGER' - Fetch when transition starts
async: 'WAIT' - Wait for resolve before entering state (default)
async: 'NOWAIT' - Don’t wait, resolve in background
Example:
{
name: 'products',
resolvePolicy: {
when: 'EAGER', // Start fetching immediately
async: 'WAIT' // Wait before entering
},
resolve: [
{
token: 'products',
deps: ['ProductService'],
resolveFn: (productService) => productService.list()
}
]
}
Views
views
Named views configuration.
views?: { [key: string]: _ViewDeclaration }
Behavior:
- Defines multiple views or targets specific named
ui-views
- Each key is a view name or view target
- Use
@ syntax to target ancestor state views
Example - Multiple views in parent template:
{
name: 'layout',
views: {
header: {
component: HeaderComponent
},
body: {
component: BodyComponent
},
footer: {
component: FooterComponent
}
}
}
Example - Target ancestor views:
{
name: 'messages.detail',
views: {
// Target header view in 'top' state
'header@top': {
component: MessageHeaderComponent
},
// Target body view in parent state
'body': {
component: MessageBodyComponent
}
}
}
Lifecycle Hooks
onEnter
Transition hook called when entering the state.
onEnter?: TransitionStateHookFn
Signature:
type TransitionStateHookFn = (
transition: Transition,
state: StateDeclaration
) => HookResult
Example:
{
name: 'mystate',
onEnter: (trans, state) => {
console.log(`Entering ${state.name}`);
const myService = trans.injector().get('MyService');
myService.trackEntry(state.name);
}
}
onExit
Transition hook called when exiting the state.
onExit?: TransitionStateHookFn
Example:
{
name: 'editor',
onExit: (trans, state) => {
const editorService = trans.injector().get('EditorService');
if (editorService.hasUnsavedChanges()) {
return editorService.confirmDiscard();
}
}
}
onRetain
Transition hook called when state is retained.
onRetain?: TransitionStateHookFn
Example:
{
name: 'parent',
onRetain: (trans, state) => {
console.log(`${state.name} is still active`);
}
}
Redirection
redirectTo
Synchronously or asynchronously redirect transitions.
redirectTo?:
| RedirectToResult
| ((transition: Transition) => RedirectToResult)
| ((transition: Transition) => Promise<RedirectToResult>)
RedirectToResult type:
type RedirectToResult =
| string
| TargetState
| { state?: string; params?: RawParams }
| void
Behavior:
- Processed as
onStart hook, before LAZY resolves
- Can be string, object, TargetState, or function
- Function can return Promise for async redirects
Example - String redirect:
{
name: 'A',
redirectTo: 'A.B'
}
Example - Object redirect:
{
name: 'C',
redirectTo: {
state: 'C.D',
params: { foo: 'index' }
}
}
Example - Function redirect:
{
name: 'E',
redirectTo: () => 'A'
}
Example - Conditional redirect:
{
name: 'F',
redirectTo: (trans) => {
if (trans.params().foo < 10) {
return { state: 'F', params: { foo: 10 } };
}
}
}
Example - Async redirect:
{
name: 'G',
redirectTo: (trans) => {
const svc = trans.injector().get('AsyncService');
return svc.getRedirectTarget(trans.params().foo);
}
}
Example - With resolve data:
{
name: 'H',
redirectTo: (trans) => {
return trans.injector().getAsync('someResolve')
.then(data => data === 'login' ? 'login' : null);
}
}
Lazy Loading
lazyLoad
Function to lazy load code.
lazyLoad?: (
transition: Transition,
state: StateDeclaration
) => Promise<LazyLoadResult>
Behavior:
- Invoked before state activation
- Transition waits for loading to complete
- Should return Promise that resolves when code is loaded
- Can return LazyLoadResult with states to register
LazyLoadResult interface:
interface LazyLoadResult {
states?: StateDeclaration[];
}
Example - Load service code:
{
name: 'abc',
lazyLoad: (transition, state) => import('./abcService')
}
Example - Future state (load module):
// Future state placeholder
{
name: 'admin.**',
url: '/admin',
lazyLoad: () => import('./admin.states')
}
// admin.states.js exports:
export default {
states: [
{
name: 'admin',
url: '/admin/:adminId',
component: AdminComponent
},
{
name: 'admin.users',
url: '/users',
component: UsersComponent
}
]
};
Example - With dependency loading:
{
name: 'reports.**',
url: '/reports',
lazyLoad: async (trans, state) => {
// Load multiple dependencies
const [states, service] = await Promise.all([
import('./reports.states'),
import('./reports.service')
]);
return { states: states.default };
}
}
Internal API
$$state
Gets the internal StateObject API.
$$state?: () => StateObject
This is an internal API subject to change without notice. Do not use in application code.
TypeScript Types
Complete Example
const userDetailState: StateDeclaration = {
// Core properties
name: 'users.detail',
url: '/:userId',
parent: 'users',
abstract: false,
// Parameters
params: {
userId: { type: 'int' },
tab: { value: 'profile', squash: true }
},
// Data and resolves
data: {
requiresAuth: true,
pageTitle: 'User Detail'
},
resolve: [
{
token: 'user',
deps: ['UserService', Transition],
resolveFn: (userService, trans) =>
userService.get(trans.params().userId)
}
],
resolvePolicy: {
when: 'LAZY',
async: 'WAIT'
},
// Views
views: {
header: { component: UserHeaderComponent },
body: { component: UserBodyComponent }
},
// Lifecycle hooks
onEnter: (trans, state) => {
console.log('Entered user detail');
},
onExit: (trans, state) => {
console.log('Exited user detail');
}
};
Framework-Specific Extensions
Different UI-Router implementations extend this interface:
Angular (@uirouter/angular):
component - Component class
template / templateUrl - Template configuration
controller / controllerAs - Controller configuration
React (@uirouter/react):
component - React component class or function
AngularJS (@uirouter/angularjs):
template / templateUrl - Template strings
controller / controllerAs - Controller configuration
templateProvider - Dynamic templates
See Also