diff --git a/Cargo.lock b/Cargo.lock
index 3562cc6..e3eeb7e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2686,6 +2686,7 @@ dependencies = [
"log",
"serde",
"serde_json",
+ "tempfile",
"tracing",
"wasm-bindgen",
"wasm-bindgen-futures",
diff --git a/Cargo.toml b/Cargo.toml
index 9634a70..1cc776f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -53,6 +53,9 @@ serde_json = "1.0.100"
futures = "0.3.31"
gloo-net = "0.6"
+[dev-dependencies]
+tempfile = "3.6"
+
[features]
default = ["web"]
web = ["dioxus/web"]
diff --git a/README.md b/README.md
index 1e629b6..129fad2 100644
--- a/README.md
+++ b/README.md
@@ -23,3 +23,93 @@ To run for a different platform, use the `--platform platform` flag. E.g.
dx serve --platform desktop
```
+## Configuration (appsettings.json and WASM fast-path)
+
+This project supports an `appsettings.json`-style configuration that is loaded at runtime.
+
+- Native (desktop/server): the app tries to read `appsettings.json` from the working directory.
+- WASM (web): the loader will first try a small HTML-injection fast-path (see below). If that
+ is not present it will fetch `appsettings.json` from the hosting origin.
+
+Example `appsettings.json`:
+
+```json
+{
+ "server": {
+ "stun_server": "stun:stun.l.google.com:19302"
+ }
+}
+```
+
+HTML injection fast-path (for embedding a small config directly in `index.html`):
+
+```html
+
+```
+
+This is useful for static hosting scenarios where you want to provide a different runtime
+configuration without rebuilding the WASM artifact.
+
+## Consuming the Config in the UI (Dioxus)
+
+The Dioxus app provides a `Signal` via the hooks context. Child components can consume
+it with:
+
+```rust
+let cfg_signal = use_context::>();
+let cfg = cfg_signal.get(); // or cfg_signal.read()/() depending on the signal API
+// use cfg.server.stun_server
+```
+
+The app uses an async loader (via `use_resource`) and overwrites the initial default config when
+the runtime-loaded config becomes available. This gives instant sensible defaults while still
+allowing runtime overrides.
+
+### Example: Provider + Consumer (Dioxus)
+
+Below is a minimal example showing how the app creates a `Signal` provider and how a
+child component consumes it. This pattern gives children an immediately-available default
+config while an async fetch can replace it at runtime.
+
+```rust
+use dioxus::prelude::*;
+use dioxus_hooks::use_resource;
+use niom_webrtc::config;
+
+#[allow(non_snake_case)]
+fn ConfigProvider(cx: Scope) -> Element {
+ // synchronous immediate default
+ let default_cfg = config::load_config_sync_or_default();
+
+ // a reactive signal holding the current config
+ let cfg_signal = use_state(cx, || default_cfg.clone());
+
+ // async resource that loads runtime config (WASM fetch or native file)
+ let resource = use_resource(cx, (), |_| async move { config::load_config_or_default().await });
+
+ // when resource completes, update the signal so children react
+ if let Some(cfg) = resource.value() {
+ cfg_signal.set(cfg.clone());
+ }
+
+ // provide the signal to descendants
+ use_context_provider(cx, || cfg_signal.clone());
+
+ cx.render(rsx! { children(&cx) })
+}
+
+fn SomeChild(cx: Scope) -> Element {
+ let cfg_signal = use_context::>(cx);
+ let cfg = cfg_signal.get();
+
+ cx.render(rsx!(div { "STUN: {cfg.server.stun_server}" }))
+}
+```
+
+Notes:
+- `load_config_sync_or_default()` returns a sensible default immediately (plus HTML fast-path on WASM).
+- `load_config_or_default()` is async and will perform a network fetch on WASM if the HTML fast-path wasn't present.
+
+
diff --git a/src/config.rs b/src/config.rs
index ea9114f..7a3493b 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -26,13 +26,74 @@ impl Config {
// WASM runtime loader: fetch `appsettings.json` from the hosting origin
#[cfg(target_arch = "wasm32")]
pub async fn load_config_from_server() -> Result> {
- // lazy import gloo-net to avoid non-wasm compile errors
+ // First try HTML-injection fast-path: look for a