UI-Router Core

Framework-agnostic state machine for URL-driven routing, transitions, and nested views in JS SPAs.

Framework
npm
v6.1.2
125stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
49/100Fair
Development Activity0
Maintenance32
Community84
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture88
Code Quality85
Innovation70
Learning Curve60

@uirouter/core is the framework-agnostic engine behind the UI-Router family of routers. Instead of matching URLs to flat page components, it models an application as a hierarchical tree of named states, each of which can own its own URL segment, parameters, resolved data, and nested views. Navigating the app means transitioning between states in a transaction-like process, with lifecycle hooks (onStart, onEnter, onRetain, onExit) that can redirect, cancel, or asynchronously wait before a transition completes.

Because the core has no dependency on any specific UI framework, it ships its own minimal location-service and dependency-injector implementations in src/vanilla so it can run standalone, while framework-specific packages such as UI-Router for AngularJS, UI-Router for Angular, and UI-Router for React plug in their own bindings via the router’s plugin API. That separation is what lets the same state-machine, URL-matching, and transition logic power routing across three otherwise unrelated frontend stacks.

What You Get

  • A hierarchical state registry (StateRegistry) for defining and querying nested application states by absolute or relative name
  • A transition service with lifecycle hooks (onBefore, onStart, onEnter, onRetain, onExit, onSuccess, onError) for guarding, redirecting, or resolving data before a navigation completes
  • A URL matching and generation layer (UrlMatcher, UrlRouter, UrlConfig) supporting typed parameters (int, string, date, json) and custom encoders/decoders
  • A view service for coordinating multiple named and nested views per state, so a single transition can update several parts of the UI at once
  • A vanilla (framework-free) location service and dependency injector so the router can run outside any specific UI framework, plus a plugin() API for binding it into one

Common Use Cases

  • Building the routing engine for a new UI framework binding by implementing LocationServices/LocationConfig and registering it via router.plugin()
  • Modeling deeply nested application layouts (e.g. dashboard > project > settings) as parent/child states with independently resolved data and views
  • Guarding navigation with async authentication/authorization checks in onStart/onEnter transition hooks
  • Defining type-safe, validated URL parameters (dates, integers, JSON blobs) that are automatically encoded/decoded between the URL and application state
  • Lazy-loading feature modules and their states only when a matching transition first occurs

Under The Hood

Architecture The UIRouter class in src/router.ts is the composition root: its constructor wires together ViewService, UIRouterGlobals, TransitionService, UrlMatcherFactory/UrlRouter/UrlService, StateRegistry, and StateService, registering each as a disposable so router.dispose() can tear the whole instance down cleanly. The state tree itself is built and validated by stateBuilder.ts/stateQueueManager.ts/stateObject.ts under src/state/, transitions are computed and executed through hookRegistry.ts/hookBuilder.ts/transitionHook.ts/rejectFactory.ts under src/transition/, and URL synchronization is handled by urlMatcher.ts/urlRule.ts/urlRouter.ts under src/url/. Crucially, none of this depends on a specific host framework — src/vanilla/ supplies a minimal LocationServices/injector implementation so the core runs standalone, and the plugin() method on UIRouter is the seam through which framework-specific packages (UI-Router for AngularJS, Angular, and React, maintained as sibling repos) inject their own location services and DI — a layered, plugin-oriented design where breaking that seam would break every downstream binding at once.

Tech Stack Written almost entirely in TypeScript (99.6% of the codebase per GitHub’s language breakdown), compiled twice via tsc (CommonJS and ES module outputs) and bundled with Rollup plus rollup-plugin-uglify for minified UMD bundles; dts-downlevel downgrades the emitted .d.ts files so they remain compatible with older TypeScript versions used by consumers. Tests run in real browsers via Karma with the Jasmine framework and Chrome/Firefox launchers rather than a DOM-simulation library. Linting is handled by ESLint with @typescript-eslint, formatting by Prettier enforced through a Husky pre-commit hook, and CI runs on GitHub Actions across a test matrix plus a dedicated downstream-projects job that exercises the AngularJS, Angular, and React UI-Router packages against the current core build. Documentation is generated and published through the sibling @uirouter/publish-scripts tooling rather than a bundled docs site.

Code Quality The test/ directory holds over 30 spec files exercising the state registry, transition pipeline, URL matcher, hooks, resolves, params, and the vanilla location services, plus two dedicated compatibility folders (typescript3.9, typescript5.4) that smoke-test the published type definitions against both an old and a current TypeScript compiler. Core modules such as state/stateService.ts and transition/transition.ts pair implementation files with sibling interface.ts files defining their public contracts, use a dedicated rejection factory (rejectFactory.ts) rather than throwing raw errors for expected transition failures, and follow consistent naming conventions enforced by the linter and formatter. No gaps in test coverage were apparent for the core routing, transition, and URL-matching paths during review.

What Makes It Unique Most JavaScript routers match a URL to a single flat route; UI-Router Core instead models the application as a hierarchical state machine, so a URL activates an entire chain of parent/child states, each with its own resolved data, parameters, and views, and navigation between them is a transaction that can be validated, redirected, or rolled back through lifecycle hooks before it commits. Combining that transactional state-machine model with a genuinely framework-agnostic core — the same router engine, unmodified, powers routing for AngularJS, Angular, and React through thin per-framework bindings — is unusual in a JavaScript ecosystem where routers are typically built framework-first.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search