The Ember mission is worked up to announce the discharge of Ember v7.3. This can be a commonplace minor launch as a part of the Ember Launch Practice course of.
This launch brings a brand new solution to create reactive state that does not want a category, makes a critical dent within the dimension of the JavaScript bundle for apps which are on the trendy construct system, and fixes a handful of long-standing bugs within the router and elsewhere! 🎉😃
Ember.js 7.3
Ember.js 7.3 introduces one new function per RFC #1071: tracked can now be used exterior of courses, and, arguably extra importantly, each types of tracked allow you to configure equality in order that setting a price to what it already was not triggers a re-render. The discharge additionally consists of some inside restructuring that lets bundlers drop much more unused code from ember-source, and ships eight bugfixes. There aren’t any new deprecations.
tracked() exterior of courses
Since Ember Octane, the best way you create reactive state in Ember has been to place a @tracked property on a category:
import Part from '@glimmer/element';
import { tracked } from '@glimmer/monitoring';
export default class Counter extends Part {
@tracked depend = 0;
increment = () => this.depend++;
}
This works nice and remains to be what we suggest for the overwhelming majority of app code, nevertheless it does imply that if you need a single reactive worth you first want a category to place it on. That will get in the best way in just a few locations: helpers and modifiers which are written as plain features, checks that desire a little bit of state to poke at, and demos the place each further line of boilerplate is a distraction from the factor you might be truly attempting to point out.
Ember 7.3 implements RFC #1071, which does two issues to the prevailing tracked import. First, if you name it as a operate with an preliminary worth it returns a standalone reactive worth usable exterior of courses:
import { tracked } from '@glimmer/monitoring';
const depend = tracked(0);
const increment = () => depend.worth++;
<template>
Rely is: {{depend.worth}}
<button {{on "click on" increment}}>add one</button>
</template>
Studying depend.worth in a template (or in a getter {that a} template makes use of) entangles with the render equally to studying a @tracked property would, and writing to it causes a re-render. Alongside .worth there are 4 operate short-hands:
depend.get(); // similar as studying depend.worth
depend.set(2); // similar as assigning depend.worth
depend.replace((n) => n + 1); // write based mostly on the present worth, with out consuming it
depend.freeze(); // stop any additional writes
One good sample that this unlocks is maintaining mutable state personal to a category whereas exposing a read-only view of it:
import { tracked } from '@glimmer/monitoring';
export class Session {
#person = tracked(null);
get person() {
return this.#person.worth;
}
async login() {
this.#person.worth = await fetchCurrentUser();
}
}
If any of this appears to be like acquainted it is as a result of the thought has been floating across the ecosystem for some time. It was prototyped as Cell in Starbeam and has been out there to Ember builders as cell from ember-resources. Now it is inbuilt, with no further import. Relatively than @tracked being “magic, we are able to make the most of tracked() as a storytelling device to demystify how @tracked works.
Configurable equality
Till now, setting a @tracked property at all times notified customers, even if you set it to the precise worth it already held, so this.depend = this.depend re-rendered the whole lot that learn depend. That was a historic alternative and there was no solution to change it. Each types of tracked now settle for an choices object with an equals operate that decides whether or not a write counts as a change.
The non-decorator type defaults to Object.is, so depend.worth = depend.worth does not re-render, and you’ll cross { equals: () => false } to get the outdated always-notify behaviour. The @tracked decorator retains its always-notify default for backwards compatibility, and now you can choose a property into equality-based notification:
class Counter {
@tracked({ equals: (a, b) => a === b }) depend = 0;
// this not causes a re-render
noop = () => (this.depend = this.depend);
}
You possibly can learn extra, together with a few edge instances round passing plain objects because the preliminary worth, within the API docs for tracked.
Launched in emberjs/ember.js PR #21471
Smaller bundles for Vite apps
Within the Ember 7.2 launch weblog we talked about setting kind: "module" on ember-source and mentioned that it wasn’t sufficient by itself to shrink your bundle. This launch is the place that begins to repay.
ember-source now declares "sideEffects": false in its bundle.json. That may be a trace to bundlers like Vite and Rollup that importing one among Ember’s inside modules by no means has unwanted side effects on another module, which suggests the bundler is free to drop any module that your app by no means truly makes use of. Together with that, a few of Ember’s internals have been restructured so {that a} small app not pulls within the outdated rendering pipeline and a few classic-era items.
The impact is critical. The compressed JavaScript for the hello-world app in Ember’s personal smoke checks went from round 64.5 kB earlier than this work began to about 37 kB with these adjustments, or roughly 42% smaller. In case you are curious concerning the particulars, the numbers are tracked in PR #21456.
This solely impacts apps that eat the ESM sources of ember-source immediately, which is each app on the Embroider and Vite construct system that has been the default since Ember 6.8. In case you are nonetheless on the basic ember-cli construct you will not see any change, which is another reason to have a look at the Vite codemod if you have not but.
Launched in emberjs/ember.js PR #21456 and PR #21462
Bug Fixes
Ember.js 7.3 introduces 8 bugfixes:
- #21203 Repair
@mannequinturns intoundefinedor adjustments to the mistaken route’s mannequin throughout Glimmer elementwillDestroy - #21591 Destroy dynamic modifiers that have been set after the preliminary render, fixing a reminiscence leak
- #21409 Repair question params set off mannequin refresh unnecessarily
- #21410 Repair question param redirects throughout lively transitions
- #21521 Deal with nullish
<LinkTo>@questionas an empty question object - #21524 Enable
CoreObject#initto be referred to as with no arguments - #21406 Add a useful assertion when
{{element}}is given an unsupported argument - #21407 Enhance
{{debugger}}message for template-only parts
A few these deserve a particular point out. The primary one fixes a bug that was reported in 2020 and, from the git historical past, has most likely existed since @mannequin was launched in Ember 3.14. If a element in a route template learn @mannequin in its willDestroy hook whilst you have been transitioning to a special route, it might see undefined or, worse, the different route’s mannequin. The repair provides a examine on the controller’s id so the outlet can’t be redirected mid-teardown, and the brand new smoke take a look at covers transitions to sibling, father or mother, cousin, and unrelated routes.
The second is a reminiscence leak that has been with us since Ember 3.25. If a dynamic modifier like {{this.mod}} began out as undefined and was set to an actual modifier after the primary render (or was swapped for a special modifier later), its destructor by no means ran when the aspect went away, so something the modifier had arrange, such because the floating-ui observers in ember-primitives, leaked. The repair registers the updating opcode with its block so teardown reaches it. The PR is tagged for backport to an LTS.
The question param fixes are a part of a bigger effort to enhance the router’s take a look at protection, and every of them closes a problem that folks have been hitting for years: father or mother routes not re-run their mannequin hook if you transition to a baby route with unchanged question params, and redirecting from beforeModel again to the identical route with totally different question params not loses these params or crashes on a direct go to. And when you have ever had a <LinkTo> blow up since you handed @question={{this.maybeParams}} and it occurred to be null, that now works as anticipated.
Lastly, a small safety hardening that’s not within the listing above: the guard that stops set() from @ember/object strolling via __proto__ and constructor in a path now additionally blocks prototype, closing a prototype air pollution edge case. See PR #21451.
Documentation
The API docs for {{every}} and {{each-in}} now doc that they assist native Set and Map respectively, which they’ve completed for the reason that iterable refactor however by no means mentioned so. See PR #21523. The Ember.Templates.helpers docs additionally hyperlink to @ember/helper accurately once more (PR #21573).
Ember CLI 7.3
Ember CLI 7.3 is a upkeep launch for ember-cli itself: no new options, no deprecations, and no bugfixes. The one adjustments there are the routine dependency updates that occur as a part of the discharge prepare: ember-cli itself picked up small updates to issues like testem and morgan, and the default app blueprint now generates apps on ember-source 7.3 with the present @embroider/macros, @glint/template, and eslint-plugin-warp-drive variations. None of these updates crossed a significant model boundary, so there may be nothing to do if you improve. As with 7.2, newly generated apps nonetheless get WarpDrive 5.8.
Checks run via testem immediately
The @ember/app-blueprint that ember new makes use of did get one change price realizing about. Since Ember 6.8, pnpm take a look at in a brand new app has constructed the app with Vite after which handed the constructed output to ember take a look at --path dist. In 7.3 that second step calls testem immediately:
"take a look at": "vite construct --mode improvement && testem ci --port 0"
with a cwd: 'dist' line added to testem.cjs so testem serves the constructed app. Nothing adjustments about how your checks are written or which browser runs them. ember take a look at was a skinny wrapper round testem for Vite apps, and now it calls testem immediately, eradicating a layer that might make it unclear which device owned the flags you have been passing. If you have already got an app you don’t want to vary something, however if you need the identical setup you possibly can copy the script and the one config line.
Launched in ember-cli/ember-app-blueprint PR #307
Thank You!
A last notice: this submit was drafted with the assistance of synthetic intelligence, and a human reviewed it in its entirety.
As a community-driven open-source mission with an formidable scope, every of those releases serves as a reminder that the Ember mission wouldn’t have been doable with out your continued assist. We’re extraordinarily grateful to our contributors for his or her efforts.

