Without a Bundler

View full source code

This example shows how the --target web flag can be used load code in a browser directly. For this deployment strategy bundlers like Webpack are not required. For more information on deployment see the dedicated documentation.

First, you'll need to add web-sys to your Cargo.toml.

[dependencies.web-sys] version = "0.3.4" features = [ 'Document', 'Element', 'HtmlElement', 'Node', 'Window', ]

Then, let's take a look at the code and see how when we're using --target web we're not actually losing any functionality!

use wasm_bindgen::prelude::*; // Called when the wasm module is instantiated #[wasm_bindgen(start)] pub fn main() -> Result<(), JsValue> { // Use `web_sys`'s global `window` function to get a handle on the global // window object. let window = web_sys::window().expect("no global `window` exists"); let document = window.document().expect("should have a document on window"); let body = document.body().expect("document should have a body"); // Manufacture the element we're gonna append let val = document.create_element("p")?; val.set_inner_html("Hello from Rust!"); body.append_child(&val)?; Ok(()) } #[wasm_bindgen] pub fn add(a: u32, b: u32) -> u32 { a + b }

Otherwise the rest of the deployment magic happens in index.html:

<html> <head> <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/> </head> <body> <!-- Note the usage of `type=module` here as this is an ES6 module --> <script type="module"> // Use ES module import syntax to import functionality from the module // that we have compiled. // // Note that the `default` import is an initialization function which // will "boot" the module and make it ready to use. Currently browsers // don't support natively imported WebAssembly as an ES module, but // eventually the manual initialization won't be required! import init, { add } from './pkg/without_a_bundler.js'; async function run() { // First up we need to actually load the wasm file, so we use the // default export to inform it where the wasm file is located on the // server, and then we wait on the returned promise to wait for the // wasm to be loaded. // // It may look like this: `await init('./pkg/without_a_bundler_bg.wasm');`, // but there is also a handy default inside `init` function, which uses // `import.meta` to locate the wasm file relatively to js file. // // Note that instead of a string you can also pass in any of the // following things: // // * `WebAssembly.Module` // // * `ArrayBuffer` // // * `Response` // // * `Promise` which returns any of the above, e.g. `fetch("./path/to/wasm")` // // This gives you complete control over how the module is loaded // and compiled. // // Also note that the promise, when resolved, yields the wasm module's // exports which is the same as importing the `*_bg` module in other // modes await init(); // And afterwards we can use all the functionality defined in wasm. const result = add(1, 2); console.log(`1 + 2 = ${result}`); if (result !== 3) throw new Error("wasm addition doesn't work!"); } run(); </script> </body> </html>

And that's it! Be sure to read up on the deployment options to see what it means to deploy without a bundler.

Using the older --target no-modules

View full source code

The older version of using wasm-bindgen without a bundler is to use the --target no-modules flag to the wasm-bindgen CLI.

While similar to the newer --target web, the --target no-modules flag has a few caveats:

With that in mind the main difference is how the wasm/JS code is loaded, and here's an example of loading the output of wasm-pack for the same module as above.

<html> <head> <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/> </head> <body> <!-- Include the JS generated by `wasm-pack build` --> <script src='pkg/without_a_bundler_no_modules.js'></script> <script> // Like with the `--target web` output the exports are immediately // available but they won't work until we initialize the module. Unlike // `--target web`, however, the globals are all stored on a // `wasm_bindgen` global. The global itself is the initialization // function and then the properties of the global are all the exported // functions. // // Note that the name `wasm_bindgen` can be configured with the // `--no-modules-global` CLI flag const { add } = wasm_bindgen; async function run() { await wasm_bindgen('./pkg/without_a_bundler_no_modules_bg.wasm'); const result = add(1, 2); console.log(`1 + 2 = ${result}`); } run(); </script> </body> </html>