angularjs - change - ui-router set page title

Set Page title using UI-Router (10)

I am migrating my AngularJS based app to use ui-router instead of the built in routing. I have it configured as shown below

.config(function($stateProvider, $urlRouterProvider) {
    .state('home', {
        url: '/home',
        templateUrl : 'views/home.html',
        data : { pageTitle: 'Home' }

    .state('about', {
        url: '/about',
        templateUrl : 'views/about.html',
        data : { pageTitle: 'About' }

How can I use the pageTitle variable to dynamically set the title of the page? Using the built in routing, I could do

$rootScope.$on("$routeChangeSuccess", function(currentRoute, previousRoute){
    $rootScope.pageTitle = $;

and then bind the variable in HTML as shown below

<title ng-bind="$root.pageTitle"></title>

Is there a similar event that I can hook into using ui-router? I noticed that there are 'onEnter' and 'onExit' functions but they seem to be tied to each state and will require me to repeat code to set the $rootScope variable for each state.

$stateChangeSuccess is now deprecated in UI-Router 1.x and disabled by default. You'll now need to use the new $transition service.

A solution isn't too difficult once you understand how $transition works. I got some help from @troig in understanding it all. Here's what I came up with for updating the title.

Put this in your Angular 1.6 application. Note that I'm using ECMAScript 6 syntax; if you are not, you'll need e.g. to change let to var.

.run(function($transitions, $window) {
    $transitions.onSuccess({}, (transition) => {
        let title =;
        if (title) {
            if (title instanceof Function) {
                title =, transition.params());
            $window.document.title = title;

Then just add a title string to your state:

    name: "foo",
    url: "/foo",
    template: "<foo-widget layout='row'/>",
    title: "Foo Page""

That will make the words "Foo Page" show up in the title. (If a state has no title, the page title will not be updated. It would be a simple thing to update the code above to provide a default title if a state does not indicate one.)

The code also allows you to use a function for title. The this used to call the function will be the state itself, and the one argument will be the state parameters, like this example:

    name: "bar",
    url: "/bar/{code}",
    template: "<bar-widget code='{{code}}' layout='row'/>",
    title: function(params) {
        return `Bar Code ${params.code}`;

For the URL path /bar/code/123 that would show "Bar Code 123" as the page title. Note that I'm using ECMAScript 6 syntax to format the string and extract params.code.

It would be nice if someone who had the time would put something like this into a directive and publish it for everyone to use.

Attaching $state to $rootscope to use anywhere in the app.['$rootScope', '$state', '$stateParams',
    function ($rootScope,   $state,   $stateParams) {

        // It's very handy to add references to $state and $stateParams to the $rootScope
        // so that you can access them from any scope within your applications.For example,
        // <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
        // to active whenever 'contacts.list' or one of its decendents is active.
        $rootScope.$state = $state;
        $rootScope.$stateParams = $stateParams;
<title ng-bind="$ + ' - ui-router'">about - ui-router</title>

I ended up with this combination of Martin's and tasseKATT's answers - simple and without any template related stuff:

$rootScope.$on("$stateChangeSuccess", function (event, toState) {
   $timeout(function () { // Needed to ensure the title is changed *after* the url so that history entries are correct.
     $window.document.title =; 

I found this way really easy:

  .state('app.staff.client', {
    url: '/client/mine',
    title: 'My Clients'})

and then in my HTML like this:

<h3>{{ $state.current.title }}</h3>

If you are using ES6, this works just fine :).

class PageTitle {
    constructor($compile, $timeout) {
        this.restrict = 'A';
        this._$compile = $compile;
        this.$timeout = $timeout;

    compile(element) {

    link(scope, element, attrs, controller) {
        let defaultTitle = attrs.pageTitle ? attrs.pageTitle : "My Awesome Sauce Site";
        let listener = function(event, toState) {
            let title = defaultTitle;
            if ( && title = + ' | ' + title;
            $('html head title').text(title);
        scope.$on('$stateChangeStart', listener);

export function directiveFactory($compile) {
    return new PageTitle($compile);

directiveFactory.injections = ['$compile', '$timeout'];

export default PageTitle;

Just update window.document.title:

.state('login', {
   url: '/login',
   templateUrl: "/Login",
   controller: "loginCtrl",
   onEnter: function($window){$window.document.title = "App Login"; }

That way 'ng-app' does not need to move up to the HTML tag and can stay on the body or lower.

The angular-ui-router-title plugin makes it easy to update the page title to a static or dynamic value based on the current state. It correctly works with browser history, too.

There is a another way of doing this by combining most of the answers here already. I know this is already answered but I wanted to show the way I dynamically change page titles with ui-router.

If you take a look at ui-router sample app, they use the angular .run block to add the $state variable to $rootScope.

// It's very handy to add references to $state and $stateParams to the $rootScope
// so that you can access them from any scope within your applications.
// For example, <li ng-class="{ active: $state.includes('contacts.list') }"> 
// will set the <li> to active whenever 'contacts.list' or one of its 
// decendents is active.

.run([ '$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
  $rootScope.$state = $state;
  $rootScope.$stateParams = $stateParams;

With this defined you can then easily dynamically update your page title with what you have posted but modified to use the defined state:

Setup the state the same way:

.state('home', {
    url: '/home',
    templateUrl : 'views/home.html',
    data : { pageTitle: 'Home' }

But edit the html a bit...

<title ng-bind="$"></title>

I can't say this is any better than the answers before... but was easier for me to understand and implement. Hope this helps someone!

Why not just:

$window.document.title = 'Title';

UPDATE: Full Directive Code

var DIRECTIVE = 'yourPageTitle';

yourPageTitle.$inject = ['$window'];
function yourPageTitle($window: ng.IWindowService): ng.IDirective {

    return {
        link: (scope, element, attrs) => {

            attrs.$observe(DIRECTIVE, (value: string) => {

                $window.document.title = value;

directive(DIRECTIVE, yourPageTitle);

Then in every page you would just include this directive:

    your-page-title="{{'somePage' | translate}}">

You are actually really close with your first answer/question. Add your title as a data object:

.state('home', {
    url: '/home',
    templateUrl : 'views/home.html',
    data : { pageTitle: 'Home' }

In your index.html bind the data directly to the page title:

<title data-ng-bind="$ + ' - Optional text'">Failsafe text</title>