Migrate to package:web
Dart's
package:web
exposes access to browser APIs,
enabling interop between Dart applications and the web.
Use
package:web
to interact with the browser and
manipulate objects and elements in the DOM.
import 'package:web/web.dart';
void main() {
final div = document.querySelector('div')!;
div.text = 'Text set at ${DateTime.now()}';
}
package:web
vs dart:html
#
The goal of
package:web
is to revamp how Dart exposes web APIs
by addressing several concerns with the existing Dart web libraries:
-
Wasm compatibility
Packages can only be compatible with Wasm if they use
dart:js_interop
anddart:js_interop_unsafe
.package:web
is based ondart:js_interop
, so by default, it's supported ondart2wasm
.Dart core web libraries, like
dart:html
anddart:svg
, are deprecated and not supported when compiling to Wasm. -
Staying modern
package:web
uses the Web IDL to automatically generate interop members and interop types for each declaration in the IDL. Generating references directly, as opposed to the additional members and abstractions indart:html
, allowspackage:web
to be more concise, easier to understand, more consistent, and more able to stay up-to-date with the future of Web developments. -
Versioning
Because it's a package,
package:web
can be versioned more easily than a library likedart:html
and avoid breaking user code as it evolves. It also makes the code less exclusive and more open to contributions. Developers can create alternative interop declarations of their own and use them together withpackage:web
without conflict.
These improvements naturally result in some
implementation differences between
package:web
and
dart:html
.
The changes that affect existing packages the most,
like IDL
renames
and
type tests,
are addressed in the migration sections that follow. While we only refer to
dart:html
for brevity, the same migration patterns apply to any other Dart
core web library like
dart:svg
.
Migrating from dart:html
#
Remove the dart:html
import and replace it with package:web/web.dart
:
import 'dart:html' as html; // Remove
import 'package:web/web.dart' as web; // Add
Add web
to the dependencies
in your pubspec:
dart pub add web
The following sections cover some of the common migration issues
from
dart:html
to
package:web
.
For any other migration issues, check the dart-lang/web repo and file an issue.
Renames
#
Many of the symbols in
dart:html
were renamed from
their original IDL declaration to align more with Dart style.
For example,
appendChild
became
append
,
HTMLElement
became
HtmlElement
, etc.
In contrast, to reduce confusion,
package:web
uses the original names from the IDL definitions.
A
dart fix
is available to convert types that have been renamed
between
dart:html
and
package:web
.
After changing the import, any renamed objects will be new "undefined" errors. You can address these either:
- From the CLI, by running
dart fix --dry-run
. -
In your IDE, by selecting the
dart fix
: Rename to 'package:web name
' .
The
dart fix
covers many of the common type renames.
If you come across a
dart:html
type without a
dart fix
to rename it,
first let us know by filing an
issue.
Then, you can try manually discovering the
package:web
type name of an
existing
dart:html
member by looking up its definition.
The value of the
@Native
annotation on a
dart:html
member definition
tells the compiler to treat any JS object of that type as the Dart class
that it annotates. For example, the
@Native
annotation tells us that the
native JS name of
dart:html
's
HtmlElement
member is
HTMLElement
,
so the
package:web
name will also be
HTMLElement
:
@Native("HTMLElement")
class HtmlElement extends Element implements NoncedElement { }
To find the
dart:html
definition for an undefined member in
package:web
,
try either of the following methods:
- Ctrl or command click the undefined name in the IDE and choose Go to Definition .
-
Search for the name in the
dart:html
API docs and check its page under Annotations .
Similarly, you might find an undefined
package:web
API whose corresponding
dart:html
member's definition uses the keyword
native
.
Check if the definition uses the
@JSName
annotation for a rename;
the value of the annotation will tell you the name the member uses in
package:web
:
@JSName('appendChild')
Node append(Node node) native;
native
is an internal keyword that means the same as
external
in this
context.
Type tests
#
It's common for code that uses
dart:html
to utilize runtime checks like
is
.
When used with a
dart:html
object,
is
and
as
verify that the object is
the JS type within the
@Native
annotation.
In contrast, all
package:web
types are reified to
JSObject
. This means a
runtime type test will result in different behavior between
dart:html
and
package:web
types.
To be able to perform type tests, migrate any
dart:html
code
using
is
type tests to use
interop methods
like
instanceOfString
or the more convenient and typed
isA
helper
(available from Dart 3.4 onward).
The
Compatibility, type checks, and casts
section of the JS types page covers alternatives in detail.
obj is Window; // Remove
obj.instanceOfString('Window'); // Add
Type signatures
#
Many APIs in
dart:html
support various Dart types in their type signatures.
Because
dart:js_interop
restricts
the types that can be written, some of
the members in
package:web
will now require you to
convert
the value before
calling the member.
Learn how to use interop conversion methods from the
Conversions
section of the JS types page.
window.addEventListener('click', callback); // Remove
window.addEventListener('click', callback.toJS); // Add
Generally, you can spot which methods need a conversion because they'll be flagged with some variation of the exception:
A value of type '...' can't be assigned to a variable of type 'JSFunction?'
Conditional imports
#
It's common for code to use a conditional import based on whether
dart:html
is supported to differentiate between native and web:
export 'src/hw_none.dart'
if (dart.library.io) 'src/hw_io.dart'
if (dart.library.html) 'src/hw_html.dart';
However, since
dart:html
is deprecated and not supported when
compiling to Wasm, the correct alternative now is to
use
dart.library.js_interop
to differentiate between native and web:
export 'src/hw_none.dart' // Stub implementation
if (dart.library.io) 'src/hw_io.dart' // dart:io implementation
if (dart.library.js_interop) 'src/hw_web.dart'; // package:web implementation
Virtual dispatch and mocking
#
dart:html
classes supported virtual dispatch, but because JS interop uses
extension types, virtual dispatch is
not possible. Similarly,
dynamic
calls
with
package:web
types won't work as expected (or, they might continue to work
just by chance, but will stop when
dart:html
is removed), as their members are
only available statically. Migrate all code that relies on virtual dispatch to
avoid this issue.
One use case of virtual dispatch is mocking. If you have a mocking class that
implements
a
dart:html
class, it can't be used to implement a
package:web
type. Instead, prefer mocking the JS object itself. See the
mocking tutorial
for more information.
Non-native
APIs
#
dart:html
classes may also contain APIs that have a non-trivial
implementation. These members may or may not exist in the
package:web
helpers. If your code relies on the specifics of that
implementation, you may be able to copy the necessary code.
However, if you think that's not tractable or if that code would be beneficial
for other users as well, consider filing an issue or uploading a pull request to
package:web
to support that member.
Zones
#
In
dart:html
, callbacks are automatically zoned.
This is not the case in
package:web
. There is no automatic binding of
callbacks in the current zone.
If this matters for your application, you can still use zones, but you will have to write them yourself by binding the callback. See #54507 for more details. There is no conversion API or helper available yet to automatically do this.
Helpers
#
The core of
package:web
contains
external
interop members,
but doesn't provide other functionality that
dart:html
provided by default.
To mitigate these differences,
package:web
contains
helpers
for additional support in handling a number of use cases
that aren't directly available through the core interop.
The helper library contains various members to expose some legacy features from
the Dart web libraries.
For example, the core
package:web
only has support for adding and removing
event listeners. Instead, you can use
stream helpers
that makes it easy to
subscribe to events with Dart
Stream
s without writing that code yourself.
// Original dart:html version:
final htmlInput = InputElement();
await htmlInput.onBlur.first;
// Migrated package:web version:
final webInput = HTMLInputElement();
await webInput.onBlur.first;
You can find all the helpers and their documentation in the repo at
package:web/helpers
. They will continuously be updated to aid users
in migration and make it easier to use the web APIs.
Examples
#
Here are some examples of packages that have been migrated from
dart:html
to
package:web
:
Unless stated otherwise, the documentation on this site reflects Dart 3.9.2. Page last updated on 2025-1-31. View source or report an issue.