Upgrading ember-cli-flash

February 9, 2026 · View on GitHub

Upgrading to v7

Configuration via Environment is Removed

This is a breaking change. The addon no longer reads configuration from config/environment.js. You should now extend the service and override the flashMessageDefaults getter.

Before (v6)

// config/environment.js
module.exports = function (environment) {
  const ENV = {
    // ...
    flashMessageDefaults: {
      timeout: 5000,
      extendedTimeout: 1000,
      priority: 200,
      sticky: false,
      showProgress: true,
      type: 'info',
      types: ['success', 'info', 'warning', 'danger', 'alert', 'secondary'],
      preventDuplicates: true,
    },
  };
  return ENV;
};

After (v7)

Create your own flash-messages service that extends the base service:

// app/services/flash-messages.ts
import { FlashMessagesService } from 'ember-cli-flash';

export default class MyFlashMessages extends FlashMessagesService {
  get flashMessageDefaults() {
    return {
      ...super.flashMessageDefaults,
      timeout: 5000,
      extendedTimeout: 1000,
      priority: 200,
      showProgress: true,
      preventDuplicates: true,
    };
  }
}

Default Options Reference

Here are all available default options you can configure:

OptionTypeDefaultDescription
timeoutnumber3000Milliseconds before flash message is automatically removed
extendedTimeoutnumber0Milliseconds to add 'exiting' class before removal (for animations)
prioritynumber100Higher priority messages appear first in arrangedQueue
stickybooleanfalseIf true, message won't auto-dismiss (must be clicked)
showProgressbooleanfalseShow a progress bar indicating time remaining
typestring'info'Default type when using add() without specifying type
typesstring[]['success', 'info', 'warning', 'danger', 'alert', 'secondary']Available type methods on the service
preventDuplicatesbooleanfalsePrevent adding duplicate messages (based on message text)
destroyOnClickbooleantrueWhether clicking the message destroys it

Configuration Examples

Example 1: Basic Customization

// app/services/flash-messages.ts
import { FlashMessagesService } from 'ember-cli-flash';

export default class FlashMessages extends FlashMessagesService {
  get flashMessageDefaults() {
    return {
      ...super.flashMessageDefaults,
      timeout: 5000,           // 5 seconds instead of 3
      showProgress: true,      // Always show progress bar
      preventDuplicates: true, // No duplicate messages
    };
  }
}

Example 2: Animated Flash Messages

For smooth animations when messages appear and disappear:

// app/services/flash-messages.ts
import { FlashMessagesService } from 'ember-cli-flash';

export default class FlashMessages extends FlashMessagesService {
  get flashMessageDefaults() {
    return {
      ...super.flashMessageDefaults,
      timeout: 4000,
      extendedTimeout: 500, // Adds 'exiting' class 500ms before removal
    };
  }
}

Then animate with CSS:

.alert {
  opacity: 0;
  transform: translateX(100px);
  transition: all 500ms ease-out;

  &.active {
    opacity: 1;
    transform: translateX(0);

    &.exiting {
      opacity: 0;
      transform: translateX(100px);
    }
  }
}

Example 3: Custom Message Types

Register custom types for your application:

// app/services/flash-messages.ts
import { FlashMessagesService } from 'ember-cli-flash';
import type { FlashObjectOptions } from 'ember-cli-flash';

export default class FlashMessages extends FlashMessagesService {
  // Declare custom types for TypeScript (base types like success, warning are already typed)
  declare notice: (message: string, options?: FlashObjectOptions) => this;
  declare error: (message: string, options?: FlashObjectOptions) => this;
  declare system: (message: string, options?: FlashObjectOptions) => this;

  get flashMessageDefaults() {
    return {
      ...super.flashMessageDefaults,
      type: 'notice',
      types: ['notice', 'success', 'error', 'warning', 'system'],
    };
  }
}

Now you can use:

this.flashMessages.notice('Check this out!');
this.flashMessages.system('Maintenance scheduled for tonight.');
this.flashMessages.error('Something went wrong!');

Example 4: Sticky Messages with Custom Close

For messages that require user interaction:

// app/services/flash-messages.ts
import { FlashMessagesService } from 'ember-cli-flash';

export default class FlashMessages extends FlashMessagesService {
  get flashMessageDefaults() {
    return {
      ...super.flashMessageDefaults,
      sticky: true,          // Don't auto-dismiss
      destroyOnClick: false, // Allow interaction without dismissing
    };
  }
}

Then in your template, use the close action:

{{#each this.flashMessages.queue as |flash|}}
  <FlashMessage @flash={{flash}} as |component flash close|>
    {{flash.message}}
    <button type="button" {{on "click" close}}>×</button>
  </FlashMessage>
{{/each}}

Example 5: High-Priority System Notifications

// app/services/flash-messages.ts
import { FlashMessagesService } from 'ember-cli-flash';

export default class FlashMessages extends FlashMessagesService {
  get flashMessageDefaults() {
    return {
      ...super.flashMessageDefaults,
      priority: 100, // Base priority
    };
  }

  // Custom method for high-priority system alerts
  systemAlert(message: string) {
    return this.add({
      message,
      type: 'danger',
      priority: 500,    // Higher than default
      sticky: true,
      showProgress: false,
    });
  }
}

TypeScript Support with Generics

The service now supports TypeScript generics for custom fields:

// app/services/flash-messages.ts
import { FlashMessagesService } from 'ember-cli-flash';

interface MyCustomFields {
  id: string;
  userId?: number;
  actionUrl?: string;
}

export default class MyFlashMessages extends FlashMessagesService<MyCustomFields> {
  get flashMessageDefaults() {
    return {
      ...super.flashMessageDefaults,
      timeout: 5000,
    };
  }
}

Then use custom fields when creating messages:

this.flashMessages.success('Saved!', {
  id: 'save-notification',
  userId: 123,
  actionUrl: '/dashboard',
});

// Find and remove by custom field
const flash = this.flashMessages.findBy('id', 'save-notification');
this.flashMessages.removeBy('userId', 123);

The FlashMessage component is also generic and infers the type from the @flash arg, giving you type-safe access to custom fields in templates:

{{#each this.flashMessages.queue as |flash|}}
  <FlashMessage @flash={{flash}} as |component flash close|>
    {{flash.message}}
    {{flash.actionUrl}}  {{! ✓ Typed as string | undefined }}
  </FlashMessage>
{{/each}}

New Methods: findBy and removeBy

Two new methods have been added to the service for finding and removing flash messages by any field:

// Find a flash message by any field
const message = this.flashMessages.findBy('id', 'notification-123');

// Remove a flash message by any field (returns boolean)
const wasRemoved = this.flashMessages.removeBy('id', 'notification-123');

Component is Now a Glimmer Component

The FlashMessage component has been converted to a Glimmer component (.gts format). The public API remains the same, but if you were extending it, you'll need to update your code.

Removed: _destroyLater Internal Method

The internal _destroyLater method has been removed. Use destroyMessage() instead.


Upgrading to v6

FlashObject is no longer an EmberObject

Most apps will be unaffected by this – unless they call getFlashObject to access flash messages.

Prior to v6 FlashObject extended EmberObject and supported methods like get, getProperties, set, and setProperties.

It's now a native class, and property access can be done using regular dot syntax, eg flash.get('message') should be replaced with flash.message.

Upgrading to v5

Test helpers

In previous versions an install-time blueprint would add a test helper to tests/helpers/flash-message.js and import it in tests/test-helper.js.

This was used to disable the timeout functionality where a flash message is removed after a delay. For most test suites this is a sensible default.

Apps in which the blueprint ran will have a tests/helpers/flash-message.js file.

Take note of whether this helper is present in your app.

Helper was present

You should remove the test helper and its import.

// tests/helpers/flash-message.js
-import { FlashObject } from 'ember-cli-flash';
-
-FlashObject.reopen({ init() {} });
// tests/test-helper.js
import Application from 'example-app/app';
import config from 'example-app/config/environment';
import * as QUnit from 'qunit';
import { setApplication } from '@ember/test-helpers';
import { setup } from 'qunit-dom';
import { start } from 'ember-qunit';
- import './helpers/flash-message';

setApplication(Application.create(config.APP));

setup(QUnit.assert);

start();

Flash messages should behave as they did before in your test runs.

Helper was not present

Try running your app tests. If they pass then you have nothing more to do.

If your tests failed then they may have been relying on flash message timeouts being enabled.

You may import and invoke the enableTimeout helper within your tests/test-helper.js. This should restore the flash message timeout behaviour that your tests expect.

// tests/test-helper.js
import Application from 'example-app/app';
import config from 'example-app/config/environment';
import * as QUnit from 'qunit';
import { setApplication } from '@ember/test-helpers';
import { setup } from 'qunit-dom';
import { start } from 'ember-qunit';
+ import { enableTimeout } from 'ember-cli-flash/test-support';

+ enableTimeout();

setApplication(Application.create(config.APP));

setup(QUnit.assert);

start();