diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md
index 5ae72e9f..675947f3 100644
--- a/design/mvp/Explainer.md
+++ b/design/mvp/Explainer.md
@@ -3013,226 +3013,7 @@ In particular, the Component Model maintains the following invariants:
## JavaScript Embedding
-### JS API
-
-The [JS API] currently provides `WebAssembly.compile(Streaming)` which take
-raw bytes from an `ArrayBuffer` or `Response` object and produces
-`WebAssembly.Module` objects that represent decoded and validated modules. To
-natively support the Component Model, the JS API would be extended to allow
-these same JS API functions to accept component binaries and produce new
-`WebAssembly.Component` objects that represent decoded and validated
-components. The [binary format of components](Binary.md) is designed to allow
-modules and components to be distinguished by the first 8 bytes of the binary
-(splitting the 32-bit [`core:version`] field into a 16-bit `version` field and
-a 16-bit `layer` field with `0` for modules and `1` for components).
-
-Once compiled, a `WebAssembly.Component` could be instantiated using the
-existing JS API `WebAssembly.instantiate(Streaming)`. Since components have the
-same basic import/export structure as modules, this means extending the [*read
-the imports*] logic to support single-level imports as well as imports of
-modules, components and instances. Since the results of instantiating a
-component is a record of JavaScript values, just like an instantiated module,
-`WebAssembly.instantiate` would always produce a `WebAssembly.Instance` object
-for both module and component arguments.
-
-Types are a new sort of definition that are not ([yet][type-imports]) present
-in Core WebAssembly and so the [*read the imports*] and [*create an exports
-object*] steps need to be expanded to cover them:
-
-For type exports, each type definition would export a JS constructor function.
-This function would be callable iff a `[constructor]`-annotated function was
-also exported. All `[method]`- and `[static]`-annotated functions would be
-dynamically installed on the constructor's prototype chain. In the case of
-re-exports and multiple exports of the same definition, the same constructor
-function object would be exported (following the same rules as WebAssembly
-Exported Functions today). In pathological cases (which, importantly, don't
-concern the global namespace, but involve the same actual type definition being
-imported and re-exported by multiple components), there can be collisions when
-installing constructors, methods and statics on the same constructor function
-object. In such cases, a conservative option is to undo the initial
-installation and require all clients to instead use the full explicit names
-as normal instance exports.
-
-For type imports, the constructors created by type exports would naturally
-be importable. Additionally, certain JS- and Web-defined objects that correspond
-to types (e.g., the `RegExp` and `ArrayBuffer` constructors or any Web IDL
-[interface object]) could be imported. The `ToWebAssemblyValue` checks on
-handle values mentioned below can then be defined to perform the associated
-[internal slot] type test, thereby providing static type guarantees for
-outgoing handles that can avoid runtime dynamic type tests.
-
-Lastly, when given a component binary, the compile-then-instantiate overloads
-of `WebAssembly.instantiate(Streaming)` would inherit the compound behavior of
-the abovementioned functions (again, using the `layer` field to eagerly
-distinguish between modules and components).
-
-For example, the following component:
-```wat
-;; a.wasm
-(component
- (import "one" (func))
- (import "two" (value string)) ๐ช
- (import "three" (instance
- (export "four" (instance
- (export "five" (core module
- (import "six" "a" (func))
- (import "six" "b" (func))
- ))
- ))
- ))
- ...
-)
-```
-and module:
-```wat
-;; b.wasm
-(module
- (import "six" "a" (func))
- (import "six" "b" (func))
- ...
-)
-```
-could be successfully instantiated via:
-```js
-WebAssembly.instantiateStreaming(fetch('./a.wasm'), {
- one: () => (),
- two: "hi", ๐ช
- three: {
- four: {
- five: await WebAssembly.compileStreaming(fetch('./b.wasm'))
- }
- }
-});
-```
-
-The other significant addition to the JS API would be the expansion of the set
-of WebAssembly types coerced to and from JavaScript values (by [`ToJSValue`]
-and [`ToWebAssemblyValue`]) to include all of [`valtype`](#type-definitions).
-At a high level, the additional coercions would be:
-
-| Type | `ToJSValue` | `ToWebAssemblyValue` |
-| ---- | ----------- | -------------------- |
-| `bool` | `true` or `false` | `ToBoolean` |
-| `s8`, `s16`, `s32` | as a Number value | `ToInt8`, `ToInt16`, `ToInt32` |
-| `u8`, `u16`, `u32` | as a Number value | `ToUint8`, `ToUint16`, `ToUint32` |
-| `s64` | as a BigInt value | `ToBigInt64` |
-| `u64` | as a BigInt value | `ToBigUint64` |
-| `f32`, `f64` | as a Number value | `ToNumber` |
-| `char` | same as [`USVString`] | same as [`USVString`], throw if the USV length is not 1 |
-| `record` | TBD: maybe a [JS Record]? | same as [`dictionary`] |
-| `variant` | see below | see below |
-| `list` | create a typed array copy for number types; otherwise produce a JS array (like [`sequence`]) | same as [`sequence`] |
-| `string` | same as [`USVString`] | same as [`USVString`] |
-| `tuple` | TBD: maybe a [JS Tuple]? | TBD |
-| `flags` | TBD: maybe a [JS Record]? | same as [`dictionary`] of optional `boolean` fields with default values of `false` |
-| `enum` | same as [`enum`] | same as [`enum`] |
-| `option` | same as [`T?`] | same as [`T?`] |
-| `result` | same as `variant`, but coerce a top-level `error` return value to a thrown exception | same as `variant`, but coerce uncaught exceptions to top-level `error` return values |
-| `map` | `new Map(_)` | `Map`s directly or other objects via `Object.entries(_)` |
-| `own`, `borrow` | see below | see below |
-| `future` | to a `Promise` | from a `Promise` |
-| `stream` | to a `ReadableStream` | from a `ReadableStream` |
-
-Notes:
-* Function parameter names are ignored since JavaScript doesn't have named
- parameters.
-* If a function's result type list is empty, the JavaScript function returns
- `undefined`. If the result type list contains a single unnamed result, then
- the return value is specified by `ToJSValue` above. Otherwise, the function
- result is wrapped into a JS object whose field names are taken from the result
- names and whose field values are specified by `ToJSValue` above.
-* In lieu of an existing standard JS representation for `variant`, the JS API
- would need to define its own custom binding built from objects. As a sketch,
- the JS values accepted by `(variant (case "a" u32) (case "b" string))` could
- include `{ tag: 'a', value: 42 }` and `{ tag: 'b', value: "hi" }`.
-* For `option`, when Web IDL doesn't support particular type
- combinations (e.g., `(option (option u32))`), the JS API would fall back to
- the JS API of the unspecialized `variant` (e.g.,
- `(variant (case "some" (option u32)) (case "none"))`, despecializing only
- the problematic outer `option`).
-* When coercing `ToWebAssemblyValue`, `own` and `borrow` handle types would
- dynamically guard that the incoming JS value's dynamic type was compatible
- with the imported resource type referenced by the handle type. For example,
- if a component contains `(import "Object" (type $Object (sub resource)))` and
- is instantiated with the JS `Object` constructor, then `(own $Object)` and
- `(borrow $Object)` could accept JS `object` values.
-* When coercing `ToJSValue`, handle values would be wrapped with JS objects
- that are instances of the handles' resource type's exported constructor
- (described above). For `own` handles, a [`FinalizationRegistry`] would be
- used to drop the `own` handle (thereby calling the resource destructor) when
- its wrapper object was unreachable from JS. For `borrow` handles, the wrapper
- object would become dynamically invalid (throwing on any access) at the end
- of the export call.
-* When an imported JavaScript function is a built-in function wrapping a Web
- IDL function, the specified behavior should allow the intermediate JavaScript
- call to be optimized away when the types are sufficiently compatible, falling
- back to a plain call through JavaScript when the types are incompatible or
- when the engine does not provide a separate optimized call path.
-
-
-### ESM-integration
-
-Like the JS API, [ESM-integration] can be extended to load components in all
-the same places where modules can be loaded today, branching on the `layer`
-field in the binary format to determine whether to decode as a module or a
-component.
-
-When present, the [`external-id`](#import-and-export-definitions) attribute of
-an `import` would be used as the [Module Specifier], thereby giving components
-the same naming expressivity as JavaScript (in particular, for importing URLs).
-In the absence of an `external-id`, the always-present, but syntactically-
-restrictive, `externname` of the import would be used instead.
-
-The main remaining question is how to deal with component imports having a
-single string as well as the new importable component, module and instance
-types. Going through these one by one:
-
-For component imports of module type, we need a new way to request that the ESM
-loader parse or decode a module without *also* instantiating that module.
-Recognizing this same need from JavaScript, there is a TC39 proposal called
-[Import Reflection] that adds the ability to write, in JavaScript:
-```js
-import Foo from "./foo.wasm" as "wasm-module";
-assert(Foo instanceof WebAssembly.Module);
-```
-With this extension to JavaScript and the ESM loader, a component import
-of module type can be treated the same as `import ... as "wasm-module"`.
-
-Component imports of component type would work the same way as modules,
-potentially replacing `"wasm-module"` with `"wasm-component"`.
-
-In all other cases, the (single) string imported by a component is first
-resolved to a [Module Record] using the same process as resolving the
-[Module Specifier] of a JavaScript `import`. After this, the handling of the
-imported Module Record is determined by the import type:
-
-For imports of instance type, the ESM loader would treat the exports of the
-instance type as if they were the [Named Imports] of a JavaScript `import`.
-Thus, single-level imports of instance type act like the two-level imports
-of Core WebAssembly modules where the first-level has been factored out. Since
-the exports of an instance type can themselves be instance types, this process
-must be performed recursively.
-
-Otherwise, function or value imports are treated like an [Imported Default Binding]
-and the Module Record is converted to its default value. This allows the following
-component:
-```wat
-;; bar.wasm
-(component
- (import "./foo.js" (func (result string)))
- ...
-)
-```
-to be satisfied by a JavaScript module via ESM-integration:
-```js
-// foo.js
-export default () => "hi";
-```
-when `bar.wasm` is loaded as an ESM:
-```html
-
-```
-
+This has been moved to [JS-Overview.md](JS-Overview.md) and [JS-Reference.md](JS-Reference.md).
## Examples
diff --git a/design/mvp/JS-Explainer.md b/design/mvp/JS-Explainer.md
new file mode 100644
index 00000000..17c2158b
--- /dev/null
+++ b/design/mvp/JS-Explainer.md
@@ -0,0 +1,264 @@
+# WebAssembly Components JS-API Explainer
+
+This explainer describes how WebAssembly Components (hereafter 'components') can be used from JS.
+
+See the [reference](./JS-Reference.md) for an in-depth walkthrough.
+
+**This is a draft and is not complete. Major details are unresolved. See "Status" at the end.**
+
+## Goals
+
+1. Components can import and use most web and JS API's
+2. Components can export an API useable by JS
+3. Components interact with the web platform in similar ways to JS:
+ a. Components can feature test whether API's are present
+ b. Components work whether they are importing a web API, or a JS polyfill, or a component polyfill
+ c. Components are tolerant of web API evolution
+ d. Components misuse of a web API's result in failure at that call-site, not link time errors
+4. Components have improved performance when calling web API's compared to today
+
+## Non-goals
+
+1. Components importing every kind of web API
+1. Components exporting any kind of JS API
+
+## Design
+
+To meet our goals, we need to define interactions (also known as 'bindings') between components, web API's, and JS.
+
+The scripting interface for web API's is handled (almost but not entirely) by WebIDL, so bindings for web API's effectively means bindings for WebIDL. WebIDL already has a "JavaScript Bindings" section which defines how JS interacts with WebIDL. There are no other bindings yet supported by WebIDL.
+
+There are roughly three paths forward here:
+
+A. Define bindings between components and JS - components transitively have access to web API's through the pre-existing JS-WebIDL bindings.
+B. (A) and also define bindings between components and WebIDL - components get a separate direct path to web API's.
+C. Define bindings between components and WebIDL - components transitively have access to JS through the pre-existing JS-WebIDL bindings.
+
+There are pros/cons to each. Let's go through them.
+
+### A. Define only bindings between Components and JS
+
+This is the smallest step from where we are today. A component's imports and exports are described in terms of JS values, and the web platform is reached the same way JS reaches it.
+
+Goals #1, #2 and #3 mostly fall out for free. Web API's are already exposed to JS, so importing one is just importing the JS function that reflects it, and exporting to JS is given by the binding. Feature testing, polyfilling and API evolution are all properties the WebIDL-JS binding already supports, so they keep working without us specifying anything new.
+
+The objection to A has always been goal #4. If a call into a web API is defined as a call through JS, JS semantics are observable at every step. Lookups on the global object and on prototypes can be intercepted, argument coercion can run user code through `valueOf`, `toString` and iterators, and the callee may be a Proxy. An engine can try to speculate these away, but that is not always easy.
+
+### B. Define bindings between Components and JS and also Components and WebIDL
+
+This is a superset of option A, so it inherits the pros/cons of that.
+
+In addition, we add a parallel binding between components and WebIDL to get goal #4 as well. Components that only need to talk to JS use the JS binding, and components that use web API's use the WebIDL binding.
+
+The cost is that we write and maintain two bindings, and they have to harmonize.
+
+### C. Define only bindings between Components and WebIDL
+
+JS already has well-defined bindings to WebIDL. If we define bindings from components to WebIDL, we get direct and efficient access to web API's (goal #4) and transitively get access to JS (goals #1 and #2).
+
+Like A we only have one specification to draft and maintain.
+
+The cost is goal #3. Feature testing, polyfills and API evolution are all things A inherits and C has to reinvent, and that is new conceptual ground.
+
+### Conclusion
+
+We should take option A. Its one disadvantage against C was goal #4, and we believe that we can work around that by carefully writing value conversion rules so that engines can fuse conversion from component values to WebIDL without any speculation.
+
+## Walkthrough
+
+### A greeter
+
+Start with a component that imports nothing:
+
+```wit
+package example:greeter;
+
+world greeter {
+ export greet: func(name: string) -> string;
+}
+```
+
+```js
+const { instance } = await WebAssembly.instantiate(bytes);
+
+instance.exports.greet("world"); // "hello, world"
+```
+
+`exports` holds one property per export and `greet` is an ordinary function. Component names are kebab-case and JS names are camelCase, so an export named `greet-loudly` would be `greetLoudly`.
+
+Arguments are converted rather than type checked, the way a WebIDL operation converts its own:
+
+```js
+instance.exports.greet(42); // "hello, 42"
+instance.exports.greet(); // TypeError
+```
+
+Passing too few arguments is a `TypeError`. Extra arguments are ignored.
+
+### A logger
+
+Now a component that imports:
+
+```wit
+package example:logger;
+
+world logger {
+ import log: func(message: string);
+ export run: func();
+}
+```
+
+```js
+const { instance } = await WebAssembly.instantiate(bytes, { log: console.log });
+
+instance.exports.run(); // logs "hello"
+```
+
+The component's `message` becomes a String and we call `log` with it. Nothing inspects what `log` is, so any callable does, and a polyfill is as good as the real thing:
+
+```js
+const lines = [];
+const log = (message) => { lines.push(message); };
+
+const { instance } = await WebAssembly.instantiate(bytes, { log });
+```
+
+Which means feature testing is just JS, done before instantiating:
+
+```js
+const log = globalThis.console?.log ?? myPolyfill;
+```
+
+### When a call fails
+
+A `result` return is not handed to JS as a value. On the way out it throws, and on the way in a thrown value is caught:
+
+```wit
+package example:parse;
+
+world parser {
+ import lookup: func(key: string) -> result;
+ export parse: func(text: string) -> result;
+}
+```
+
+```js
+const { instance } = await WebAssembly.instantiate(bytes, {
+ lookup: (key) => { throw `no such key: ${key}`; },
+});
+
+instance.exports.parse("42"); // 42
+
+try {
+ instance.exports.parse("$name");
+} catch (e) {
+ e instanceof WebAssembly.ComponentError; // true
+ e.payload; // "no such key: name"
+}
+```
+
+`payload` is the `E` value converted to JS. In the other direction the thrown JS value is converted to `E`, so `lookup` returns `result.error("no such key: name")` and the component is free to handle it instead of propagating it.
+
+An import that throws where the component asked for a plain return type has nowhere to put the error, and traps.
+
+### Importing a resource
+
+Components see JS objects as resources. A resource type import and the functions on it are satisfied by a single JS value, the constructor:
+
+```wat
+(component
+ (import "element" (type $element (sub resource)))
+ (import "[method]element.query-selector" (func
+ (param "self" (borrow $element)) (param "selectors" string)
+ (result (option (own $element)))))
+ (import "[method]element.get-attribute" (func
+ (param "self" (borrow $element)) (param "name" string)
+ (result (option string))))
+ (export "find" (func
+ (param "root" (borrow $element)) (param "selectors" string)
+ (result (option string))))
+)
+```
+
+```js
+const { instance } = await WebAssembly.instantiate(bytes, { element: Element });
+
+instance.exports.find(document.body, "h1"); // "page-title" or null
+```
+
+`Element` covers the type and both methods. The type import checks for `@@isWasmResourceOf`, and the methods are read off `Element.prototype` under their camelCase names, which is where JS finds them too.
+
+Because `find` takes a `borrow` of the *imported* type, JS keeps passing raw elements. Passing anything else fails the same brand check and is a `TypeError`, and `option` comes back as `null`.
+
+### Exporting a resource
+
+A resource a component defines and exports becomes a class:
+
+```wit
+package example:counter;
+
+world w {
+ export api: interface {
+ resource counter {
+ constructor();
+ increment: func() -> u32;
+ }
+ }
+}
+```
+
+```js
+const { instance } = await WebAssembly.instantiate(bytes);
+const { Counter } = instance.exports.api;
+
+using c = new Counter();
+c.increment(); // 1
+c.increment(); // 2
+```
+
+Type names are PascalCase, so `counter` is `Counter`. `new` runs the component's `constructor`, methods live on `Counter.prototype`, and `Symbol.dispose` drops the handle. Dropping is what runs the component's destructor, so a `Counter` nobody disposes is dropped when it is collected, through a `FinalizationRegistry`.
+
+A `borrow` the component hands out is different: it is only valid for the duration of the call it appeared in, and using it afterwards is a `TypeError`.
+
+### Loading with ESM
+
+[ESM-integration](https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration) extends to components. The loader branches on the `layer` field of the binary, so a component loads anywhere a module does today.
+
+Each component import becomes a JS import, and its module specifier is the import's [`external-id`](Explainer.md#import-and-export-definitions) if it has one and its name otherwise:
+
+```wit
+world my-component {
+ @external-id("https://esm.unpkg.com/slugify@1.6.6")
+ import slugify: func(text: string) -> string;
+}
+```
+
+## Values at a glance
+
+| Component type | JS |
+|---|---|
+| `bool` | Boolean |
+| `s8`-`s32`, `u8`-`u32` | Number, an exact integer |
+| `s64`, `u64` | BigInt |
+| `f32`, `f64` | Number, including NaN and infinities |
+| `char` | String of exactly one Unicode scalar value |
+| `string` | String, well formed |
+| `list` | `Uint8Array` |
+| `list`, `list`, `tuple` | Array |
+| `record { a-b: T }` | null-prototype object, `{ aB }` |
+| `flags "a" "b"` | null-prototype object of Booleans, `{ a, b }` |
+| `enum "a" "b"` | String, the label verbatim |
+| `option` | `null`, or the payload |
+| `variant`, `option