Skip to main content

Overview

The StateRegistry is responsible for managing all registered states in your UI-Router application. It provides methods to register, deregister, retrieve states, and listen for registry changes. Access the state registry via router.stateRegistry.

State Management Methods

register

Adds a state to the registry.
register(stateDefinition: StateDeclaration): StateObject
Parameters:
  • stateDefinition - The state declaration object to register
Return value: The internal StateObject. If successfully registered, the object is fully built. If only queued, the object is not fully built yet. Behavior:
  • Registers a StateDeclaration immediately if its parent exists
  • Queues the state for registration if its parent isn’t yet registered
  • Automatically registers queued child states when parent is registered
Example - Basic registration:
const homeState = {
  name: 'home',
  url: '/home',
  component: HomeComponent
};

const stateObj = stateRegistry.register(homeState);
Example - Nested states:
// Parent state
stateRegistry.register({
  name: 'users',
  url: '/users',
  component: UsersComponent
});

// Child state (registered after parent)
stateRegistry.register({
  name: 'users.detail',
  url: '/:userId',
  component: UserDetailComponent
});
Example - With explicit parent:
stateRegistry.register({
  name: 'detail',
  parent: 'users',
  url: '/:userId',
  component: UserDetailComponent
});
// Creates 'users.detail' state

deregister

Removes a state from the registry.
deregister(stateOrName: StateOrName): StateObject[]
Parameters:
  • stateOrName - The state’s name or state object to deregister
Return value: An array of deregistered StateObjects (including children). Behavior:
  • Removes the specified state from the registry
  • Automatically removes all child states
  • Removes associated URL rules
  • Triggers 'deregistered' event for listeners
Example - Deregister by name:
const removed = stateRegistry.deregister('admin');
console.log(`Removed ${removed.length} states`);
Example - Deregister by object:
const state = stateRegistry.get('admin');
if (state) {
  stateRegistry.deregister(state);
}
Example - Remove feature module:
// Removes 'admin' and all 'admin.**' child states
stateRegistry.deregister('admin');

get

Gets registered StateDeclaration object(s).
// Get all states
get(): StateDeclaration[]

// Get specific state
get(stateOrName: StateOrName, base?: StateOrName): StateDeclaration
Parameters:
  • stateOrName - Optional. State name (absolute or relative) or state object
  • base - Optional. Base state for resolving relative references
Return value:
  • If no arguments: array of all registered StateDeclaration objects
  • If state specified: the matching StateDeclaration, or null if not registered
Example - Get all states:
const allStates = stateRegistry.get();
console.log(`Total states: ${allStates.length}`);

allStates.forEach(state => {
  console.log(`State: ${state.name}, URL: ${state.url}`);
});
Example - Get specific state:
const homeState = stateRegistry.get('home');
if (homeState) {
  console.log(`Home URL: ${homeState.url}`);
} else {
  console.log('Home state not registered');
}
Example - Relative reference:
// Get 'parent.child' state
const childState = stateRegistry.get('.child', 'parent');

// Get parent state using '^'
const parentState = stateRegistry.get('^', 'parent.child');

root

Gets the implicit root state.
root(): StateObject
Return value: The root StateObject. Behavior:
  • Returns the root of the state tree
  • The root state is implicitly created by UI-Router
  • Has name '' (empty string) and URL '^'
  • All states are descendants of the root
This returns the internal StateObject representation, not a StateDeclaration.
Example:
const rootState = stateRegistry.root();
console.log(`Root state: ${rootState.name}`);
console.log(`Root URL: ${rootState.url}`);

Change Listeners

onStatesChanged

Listen for state registry events.
onStatesChanged(listener: StateRegistryListener): () => void
Parameters:
  • listener - Callback function invoked when states are registered/deregistered
Listener signature:
type StateRegistryListener = (
  event: 'registered' | 'deregistered',
  states: StateDeclaration[]
) => void
Return value: A deregistration function. Behavior:
  • Called whenever states are registered or deregistered
  • Receives the event type and array of affected states
  • Use returned function to stop listening
Example - Track all states:
let allStates = stateRegistry.get();

const deregisterFn = stateRegistry.onStatesChanged((event, states) => {
  switch (event) {
    case 'registered':
      states.forEach(state => {
        allStates.push(state);
        console.log(`State registered: ${state.name}`);
      });
      break;
      
    case 'deregistered':
      states.forEach(state => {
        const idx = allStates.indexOf(state);
        if (idx !== -1) {
          allStates.splice(idx, 1);
          console.log(`State deregistered: ${state.name}`);
        }
      });
      break;
  }
});

// Later, stop listening
deregisterFn();
Example - Lazy loading notification:
stateRegistry.onStatesChanged((event, states) => {
  if (event === 'registered') {
    console.log(`Lazy loaded ${states.length} states:`);
    states.forEach(s => console.log(`  - ${s.name}`));
  }
});

State Builder API

decorator

Registers a BuilderFunction for a specific StateObject property.
decorator(property: string, builderFunction: BuilderFunction): () => void
Parameters:
  • property - The name of the StateObject property (e.g., 'parent', 'url', 'path')
  • builderFunction - The function that builds the property
Return value: A deregistration function. Behavior:
  • Registers a function to build/process a specific state property
  • Multiple BuilderFunctions can be registered for the same property
  • BuilderFunctions are used when building StateObject from StateDeclaration
  • Applied to all subsequently registered states
BuilderFunction signature:
type BuilderFunction = (
  state: StateObject,
  parent?: StateObject
) => any
Example - Add custom property:
const deregister = stateRegistry.decorator('myProperty', (state, parent) => {
  // Access state declaration
  const myValue = state.self.myProperty;
  
  // Can inherit from parent
  if (!myValue && parent) {
    return parent.myProperty;
  }
  
  return myValue || 'default';
});
Example - Process resolve configuration:
stateRegistry.decorator('resolve', (state) => {
  // Transform resolve configuration
  const resolveConfig = state.self.resolve || {};
  
  // Add automatic resolves
  return {
    ...resolveConfig,
    $state$: () => state
  };
});
Example - Data inheritance:
stateRegistry.decorator('data', (state, parent) => {
  const parentData = parent?.data || {};
  const stateData = state.self.data || {};
  
  // Merge parent and state data
  return { ...parentData, ...stateData };
});

Root State Configuration

The registry automatically creates a root state with the following configuration:
{
  name: '',
  url: '^',
  views: null,
  params: {
    '#': { value: null, type: 'hash', dynamic: true }
  },
  abstract: true
}
Properties:
  • name: Empty string (root of hierarchy)
  • url: '^' (matches any URL)
  • abstract: Cannot be directly activated
  • params: Includes hash parameter for URL fragments

TypeScript Types

StateOrName

type StateOrName = string | StateDeclaration | StateObject
Accepts a state name string, StateDeclaration object, or StateObject instance.

StateRegistryListener

type StateRegistryListener = (
  event: 'registered' | 'deregistered',
  states: StateDeclaration[]
) => void
Callback function for state registry change events.

BuilderFunction

type BuilderFunction = (
  state: StateObject,
  parent?: StateObject
) => any
Function that builds or processes a StateObject property.

Common Patterns

Bulk registration

const states = [
  { name: 'home', url: '/home', component: HomeComponent },
  { name: 'about', url: '/about', component: AboutComponent },
  { name: 'contact', url: '/contact', component: ContactComponent }
];

states.forEach(state => stateRegistry.register(state));

Lazy load module states

function lazyLoadAdminModule(transition) {
  return import('./admin.states').then(module => {
    // Register all admin states
    module.adminStates.forEach(state => {
      stateRegistry.register(state);
    });
    
    return { states: module.adminStates };
  });
}

// Register future state
stateRegistry.register({
  name: 'admin.**',
  url: '/admin',
  lazyLoad: lazyLoadAdminModule
});

Feature toggle states

function registerFeature(featureName, states) {
  states.forEach(state => stateRegistry.register(state));
  
  return () => {
    // Deregister when feature disabled
    states.forEach(state => {
      stateRegistry.deregister(state.name);
    });
  };
}

const unregisterBeta = registerFeature('beta', betaStates);

// Later, disable feature
if (!isBetaEnabled) {
  unregisterBeta();
}

Find states by criteria

function findStatesByData(key, value) {
  return stateRegistry.get().filter(state => {
    return state.data && state.data[key] === value;
  });
}

// Find all states requiring authentication
const protectedStates = findStatesByData('requiresAuth', true);

Validate state configuration

stateRegistry.onStatesChanged((event, states) => {
  if (event === 'registered') {
    states.forEach(state => {
      if (!state.url && !state.abstract) {
        console.warn(`State ${state.name} has no URL`);
      }
      
      if (state.data?.requiresAuth && !state.resolve?.user) {
        console.warn(`Protected state ${state.name} missing user resolve`);
      }
    });
  }
});

State Hierarchy

States form a tree hierarchy based on their names:
// Register parent and children
stateRegistry.register({ name: 'app', url: '/app' });
stateRegistry.register({ name: 'app.home', url: '/home' });
stateRegistry.register({ name: 'app.about', url: '/about' });
stateRegistry.register({ name: 'app.about.team', url: '/team' });

// Hierarchy:
// '' (root)
//   └── app
//       ├── app.home
//       └── app.about
//           └── app.about.team
URL composition:
  • app = /app
  • app.home = /app/home
  • app.about = /app/about
  • app.about.team = /app/about/team

Internal Properties

The StateRegistry has several internal properties (marked with underscore prefix):
  • matcher - StateMatcher for finding states
  • builder - StateBuilder for building StateObjects
  • stateQueue - StateQueueManager for queued registrations
  • listeners - Array of StateRegistryListener functions
These are internal APIs and subject to change. Do not access directly.

See Also

Build docs developers (and LLMs) love