Getting started with JavaScript interop
This tutorial teaches you the basics of interacting with JavaScript in Dart code by using various browser and JavaScript APIs.
Access and define a JavaScript object
#
To access a global JavaScript object, such as the browser's
document object, declare an @JS-annotated, external
top-level getter. This returns the value as an opaque JSObject.
import 'dart:js_interop';
// All top-level JS interop APIs need the @JS annotation.
@JS()
external JSObject get document;
A JSObject is opaque and doesn't provide type safety or auto-completion.
To add type-safe members, define an interop type using an extension type.
This acts as an interface, allowing you to declare more interop APIs as
external members. You can define an interop type using
extension types to view it differently:
@JS()
external Document get document;
extension type Document._(JSObject _) implements JSObject {}
Now, you can add external methods to your Document interface. For example,
add the createElement() instance method:
extension type Document._(JSObject _) implements JSObject {
external JSObject createElement(JSString tag);
}
With the createElement method defined, you can call it on the
document object. Notice that you must convert 'button'
from
a String to a JSString using the .toJS extension method.
var button = document.createElement('button'.toJS);
Values passed to and from interop APIs must be either an
interop type (like JSObject or JSString) or an allowed
Dart primitive.
Use automatic type conversions
#
The compiler automatically converts most Dart primitive types
(like String, num, bool, and null), so you can often
use them directly in interop signatures to simplify your code.
For example, you can rewrite the createElement declaration
from the previous section to accept a Dart String directly.
For example:
external JSObject createElement(String tag);
Now, you can call it without the explicit .toJS conversion.
For example:
var button = document.createElement('button');
To add the newly created button to the document's body, first
define interop types for the body and its appendChild()
method:
extension type Document._(JSObject _) implements JSObject {
external JSObject createElement(String tag);
external Body get body;
}
extension type Body._(JSObject _) implements JSObject {
external JSObject appendChild(JSObject child);
}
With these definitions, you can create a button and add it to the page:
var button = document.createElement('button');
document.body.appendChild(button);
Handle events and callbacks
#
To handle user interactions, such as a button click,
you can register an event listener using addEventListener().
First, create an interface for a button element. Then, call
addEventListener with the event name and a callback function.
extension type ButtonElement(JSObject _) implements JSObject {
external void addEventListener(String event, JSFunction listener);
}
var button = ButtonElement(document.createElement('button'));
document.body.appendChild(button);
button.addEventListener('click', (JSObject event) {
print('Clicked!');
}.toJS);
Callbacks converted to JS with .toJS have the same
type limitations as other interop APIs in that their parameters
and return values must be interop types or compatible primitives.
Work with Promises and Arrays
#
JavaScript interop provides helpers for other common types, like
converting JavaScript Promises to and from Dart Futures, and
Arrays to and from Lists.
Promises and futures
#
This example uses the fetch API, which returns a Promise.
The .toDart extension converts the Promise to a Future,
so you can await its result in Dart:
import 'dart:js_interop';
extension type Response._(JSObject _) implements JSObject {
external bool get ok;
}
@JS()
external Response fetch(String resource);
void main() async {
var response = await fetch('image.png').toDart;
print(response.ok);
}
Arrays and Lists
#
This example calls the static JavaScript Array.of method
to create a JSArray. It then converts the array to a
Dart List, iterates over it, and prints each element.
import 'dart:js_interop';
@JS('Array.of')
external JSArray<JSString> arrayOf(String a, String b);
void main() {
var array = arrayOf('hello', 'world');
var list = array.toDart;
for (var element in list) {
print(element.toDart);
}
}
When converting a generic type like a List, its elements
must already be JS interop types. For example, to convert
a List<String>, you must first convert each String
into a JSString.
// Option 1: Create the list with JS types initially.
List<JSString> list = ['hello'.toJS, 'world'.toJS];
JSArray jsArray1 = list.toJS;
// Option 2: Map a Dart list to a list of JS types.
List<String> dartList = ['hello', 'world'];
JSArray jsArray2 = dartList.map((e) => e.toJS).toList().toJS;
Learn more
#- For more information on type conversions, check out Conversions.
- For more information on how to write interop APIs, see the Usage guide.
-
To access common utility functions, see:
- The
dart:js_interoplibrary, and - The
dart:js_interop_unsafelibrary.
- The
-
The
package:webexposes many of the browser APIs (including those used in the above examples) through interop declarations.
Unless stated otherwise, the documentation on this site reflects Dart 3.9.2. Page last updated on 2025-8-7. View source or report an issue.