{"id":18016674,"url":"https://github.com/madoshakalaka/yew-interop","last_synced_at":"2025-08-26T22:07:09.127Z","repository":{"id":37858376,"uuid":"440743383","full_name":"Madoshakalaka/yew-interop","owner":"Madoshakalaka","description":"Load Javascript and CSS asynchronously in yew","archived":false,"fork":false,"pushed_at":"2023-03-13T14:06:04.000Z","size":14100,"stargazers_count":43,"open_issues_count":6,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-22T07:23:04.260Z","etag":null,"topics":["codesplitting","interop","rust","wasm","webassembly","yew"],"latest_commit_sha":null,"homepage":"https://madoshakalaka.github.io/yew-interop","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Madoshakalaka.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-12-22T05:28:06.000Z","updated_at":"2025-03-09T11:57:07.000Z","dependencies_parsed_at":"2024-04-10T04:38:18.198Z","dependency_job_id":"432f34e2-12e9-43ba-b1cb-227576da1773","html_url":"https://github.com/Madoshakalaka/yew-interop","commit_stats":{"total_commits":189,"total_committers":4,"mean_commits":47.25,"dds":0.455026455026455,"last_synced_commit":"99b8dc9880b8edad5f45ba0e4ec3c98393505a26"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Madoshakalaka%2Fyew-interop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Madoshakalaka%2Fyew-interop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Madoshakalaka%2Fyew-interop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Madoshakalaka%2Fyew-interop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Madoshakalaka","download_url":"https://codeload.github.com/Madoshakalaka/yew-interop/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245713125,"owners_count":20660347,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codesplitting","interop","rust","wasm","webassembly","yew"],"created_at":"2024-10-30T04:18:58.733Z","updated_at":"2025-03-26T18:32:28.812Z","avatar_url":"https://github.com/Madoshakalaka.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e \u003ch1\u003eYew Interop\u003c/h1\u003e \u003cp\u003e \u003cstrong\u003eLoad JavaScript and CSS asynchronously in Yew\u003c/strong\u003e \u003c/p\u003e\n  \u003ca href=\"https://crates.io/crates/yew-interop\"\u003e\u003cimg alt=\"Crates.io\" src=\"https://img.shields.io/crates/v/yew-interop\"\u003e\u003c/a\u003e\n\u003ca href=\"https://madoshakalaka.github.io/yew-interop/v0.3.0\"\u003e\u003cimg alt=\"demo badge released\" src=\"https://img.shields.io/badge/demo-v0.3.0-brightgreen\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://madoshakalaka.github.io/yew-interop/master\"\u003e\u003cimg alt=\"demo badge master\" src=\"https://img.shields.io/badge/demo-master-brightgreen\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://docs.rs/yew-interop\"\u003e\u003cimg alt=\"docs.rs\" src=\"https://img.shields.io/badge/docs.rs-released-brightgreen\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://madoshakalaka.github.io/yew-interop/docsrs/yew_interop/\"\u003e\u003cimg alt=\"docs master\" src=\"https://img.shields.io/badge/docs-master-brightgreen\"/\u003e\u003c/a\u003e\n\u003ca href=\"#\"\u003e\u003cimg alt=\"minimal rustc\" src=\"https://img.shields.io/badge/rustc-1.56%2B-lightgrey\"/\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n## Load On Demand\n\nWith `yew-interop`, each resource is requested on demand when a consuming component requests it.\n\n\nIf you include your libraries using the\n[JS-snippet method with wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/reference/js-snippets.html)\nor insert them as `\u003cscript/\u003e` or `\u003clink/\u003e` directly in the `index.html`,\nthe resources will load for every request,\neven if there is no consuming component.\nThis can cause congestion and wasted data.\n\n\n## Load Once, Use Everywhere\n\nEach resource is strictly requested once.\nIf a resource has been requested in one component,\nother consuming components won't trigger a reload.\nOther requests to the same resource will either wait for the load to complete,\nor return ready immediately if the resource is loaded.\n\n\n## Demo\n\n[The example folder](https://github.com/Madoshakalaka/yew-interop/tree/master/example)\nhas a demo website built with`yew-interop`\n\nThe gif below shows the first two use cases,\nloading speed is throttled for demo purposes.\n\n![yew interop demo gif](https://madoshakalaka.github.io/yew-interop/master/static/yew-interop-demo.gif)\n\n[Check out the full online demo](https://madoshakalaka.github.io/yew-interop/master)\n\n# Install\nThe master branch has the the lastest in-development code.\n\n```toml\nyew-interop = {git=\"https://github.com/Madoshakalaka/yew-interop.git\", branch=\"master\", features=[\"yew-stable\"]}\n```\n\nThe `yew-stable` feature works with the latest release of yew on crates.io, currently 0.19.\nIf you are using yew-next (yew's master branch), change the `yew-stable` feature to `yew-next`.\n\nOr you can install the latest version published on crates.io, which uses yew 0.19.\n\n```toml\nyew-interop = \"0.3\"\n```\n\nNote the `yew-next`/`yew-stable` features only exist in the master branch\nsince published crates can't have git dependencies.\n\n# API\n\n## Asynchronously Load CSS or Javascript Libraries\n\nIf your javascript library exposes functions or objects you want to use in Rust,\nthen `yew_interop::declare_resources!` is the right choice.\n\nFirst you want to create a separate module `interop` and declare your dependencies there.\n\n```rust\n// alternatively, create a separate file `interop.rs`,\n// and add `mod interop;` to `main.rs` to have tidier code.\nmod interop{\n    use yew_interop::declare_resources;\n\n    declare_resources!(\n        library_a\n        \"https://my-cdn.com/library-a.min.js\"\n        library_b\n        \"https://my-cdn.com/library-b.min.js\"\n        \"https://my-cdn.com/library-b.min.css\"\n        library_c\n        \"/static/library-c.min.js\"\n        \"/static/library-c.min.css\"\n    );\n}\n```\nThis macro expands into a `\u003cResourceProvider/\u003e` component.\nyou want to wrap the root of your application in the provider:\n\n```rust\nuse yew::prelude::*;\nuse interop::ResourceProvider;\n\n#[function_component(App)]\npub fn app() -\u003e Html {\n    html! {\n        \u003cResourceProvider\u003e\n            // the rest of your app\n        \u003c/ResourceProvider\u003e\n    }\n}\n```\nThe macro will also expand into hooks by prepending your resource names with \"_use__\", in this case,\nthe macro will expand into `pub fn use_library_a() -\u003e bool` and `pub fn use_library_b() -\u003e bool`\n\nAt your consuming component, you can use these hooks to asynchronously wait for libraries to be loaded:\n\n```rust\n\nuse yew::prelude::*;\nuse interop::use_library_a;\n\n#[function_component(Consumer)]\npub fn consumer() -\u003e Html {\n    let library_a_ready = use_library_a(); // \u003c-- generated hook\n\n    html! {\n        if library_a_ready{\n            // use library a here\n        }else{\n            \u003cp\u003e{\"please wait...\"}\u003c/p\u003e\n        }\n    }\n}\n```\n\u003eFor javascript libraries,\nyou will also need to write some stubs using `wasm-bindgen` and `js-sys` before using the library in Rust.\nThe wasm-bindgen book has [a good chapter](https://rustwasm.github.io/wasm-bindgen/examples/import-js.html) on that.\nYou can also check out our demo website and have a look [how it's done there](https://github.com/Madoshakalaka/yew-interop/blob/master/example/src/interop.rs)\n\n## Explicit Resource Type\n\nThe `declare_resources!` macro needs to know whether a url is JavaScript or CSS.\nWhen you provide a string literal as in the examples above,\nthe macro derives the information from the suffix of the last path segment.\nIt expects .js or .css and is smart enough to exclude the query params or the fragment.\n\nWhen the path segment doesn't end with .js or .css,\nor when you provide other expressions like a macro call or an identifier,\nyou need to manually specify the URL type by prepending the custom keyword js/css\nbefore the url.\n\n`declare_resources!` will accept any expression with a return type that implements `Into\u003cCow\u003c'static, str\u003e\u003e`,\nso `\u0026'static str`, `String`, `Cow\u003c'static, str\u003e` are all fine.\n\nhere's a more complex example:\n\n```rust\nuse yew_interop::declare_resources;\n\nconst MY_LIB_JS: \u0026str = \"https://cdn.com/my_lib.js\";\n\ndeclare_resources!(\n        my_lib\n        js MY_LIB_JS\n        \"https://cdn.com/my_lic_b.css\" // \u003c-- when a string literal is provided, script type is determined from the suffix\n        \"/static/snippet.js\"\n        js concat!(\"https://a.com/\", \"b.js\")\n        my_lib_b\n        css \"/somehow/ends/with/.js\" // \u003c-- explicit type css overrides the suffix\n        my_lib_c\n        js String::from(\"https://a.com/test.js\")\n    );\n\n```\n## Side Effect Javascript\n\nHere, side effect scripts refers to the JavaScript that run something onload,\nas opposed to a library that exposes functions and classes.\n\nIf your javascript is a side effect script,\nyou want to enable the `script` feature.\n\n\n```toml\n# change yew-stable to yew-next if you use yew's master branch\nyew-interop = {git=\"https://github.com/Madoshakalaka/yew-interop.git\",  features=[\"yew-stable\", \"script\"]}\n```\nor\n```toml\nyew-interop = {version = \"0.3\", features = [\"script\"]}\n```\n\nYou will need to prepend the identifier of a script with an exclamation mark (!).\nAnd only one script url for each identifier, here's an example:\n\n```rust\nuse yew_interop::declare_resources;\n    \ndeclare_resources!(\n   lib // \u003c- normal library\n   \"https://cdn.com/lib.js\"\n   \"/static/lib.css\"\n   ! my_script // \u003c- exclamation mark for side effect scripts\n   \"https://cdn.com/script.js\"\n);\n```\nYou never need to specify the resource type explicitly,\nsince only JavaScript is allowed.\n\nSame as previous examples, this will expand into a `use_\u003cidentifier\u003e` hook.\nWhat's different is that instead of a bool,\nthe hook returns an `Option\u003cScript\u003e`,\nit is `None` when the script is loading.\n\nTo run the script, you will need to render a `\u003cScriptEffect/\u003e` component and pass\nthe script object to the component.\nThis allows you to freely control whether and when the script should be run.\nThe `\u003cScriptEffect/\u003e` component is a [portal](https://yew.rs/docs/next/advanced-topics/portals)\nto the `\u003chead/\u003e` element of the document,\nso it won't render anything in its place,\nit will only run the script on render.\n```rust\nmod interop{\n   use yew_interop::declare_resources;\n   declare_resources!(\n       ! my_script\n       \"https://cdn.com/script.js\"\n   );\n}\n\nuse yew::prelude::*;\nuse yew_interop::ScriptEffect;\nuse interop::use_my_script; // \u003c-- generated hook\n\n/// this example simply runs the script on every re-render, if the script is ready.\n#[function_component(MyComp)]\npub fn my_comp() -\u003e Html {\n    let script = use_my_script(); // \u003c-- returns Option\u003cScript\u003e\n\n    // ...snip\n\n    html! {\n        if let Some(script) = script{\n           \u003cScriptEffect {script}/\u003e\n        }else{\n           \u003cp\u003e{\"Please wait...\"}\u003c/p\u003e\n        }\n    }\n}\n```\nIf your script depends on other components being rendered,\nsuch as the fourth example [in the demo](https://madoshakalaka.github.io/yew-interop/master/),\nwhere the script adds onclick handlers to the rendered elements,\nyou will need to guarantee the script is rendered after all the dependees.\n\nYew renders descendents in a breadth-first order from bottom to top,\nwhich is not the most intuitive rendering order.\n\nOne way to guarantee the correct rendering order is to\nplace the `\u003cScriptEffect/\u003e` component as a **sibling on top of the deepest dependees**,\n\nFor example, let's say your script depends on two components `\u003cComponentA/\u003e` and `\u003cComponentB/\u003e`.\n\nThe case below shows a correct placement where A and B has the same depth,\n\n```rust\n   html!{\n       \u003c\u003e\n       \u003cScriptEffect {script}/\u003e\n       \u003cComponentA/\u003e\n       \u003cComponentB/\u003e\n       // \u003cScriptEffect {script}/\u003e !!! do not place here, otherwise it would render first\n       \u003c/\u003e\n   }\n```\nthe rendering order here is B -\u003e A -\u003e ScriptEffect.\n\nHere's trickier one, where B is deeper, so we place our component on top of B:\n\n```rust\n#[derive(Properties, PartialEq)]\npub struct ContainerProps {\n   children: Children\n}\n\n#[function_component(Container)]\npub fn container(props: \u0026ContainerProps) -\u003e Html {\n   // --snip--\n   html! {\n       {for props.children.iter()}\n   }\n}\n\nhtml!{\n    \u003c\u003e\n    \u003cComponentA/\u003e\n    \u003cContainer\u003e\n        \u003cScriptEffect {script}/\u003e\n        \u003cComponentB/\u003e\n    \u003c/Container\u003e\n    \u003cComponentC/\u003e\n    \u003c/\u003e\n}\n```\nThe rendering order is C -\u003e Container -\u003e A -\u003e B -\u003e ScriptEffect.\n\n# Contributing\n\nYour pull request is welcome!\nThere is extensive testing in CI.\nBe sure to check out our [development guide](https://github.com/Madoshakalaka/yew-interop/blob/master/CONTRIBUTING.md).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmadoshakalaka%2Fyew-interop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmadoshakalaka%2Fyew-interop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmadoshakalaka%2Fyew-interop/lists"}