Browser Storage and Navigation in euv
Project Code:https://github.com/euv-dev/euv
When building web applications, two common requirements are persistent data storage and URL-based navigation. euv provides direct access to browser APIs through its window() function, allowing you to interact with localStorage, sessionStorage, and the browser's Location object. This article covers how to use these APIs effectively within the euv framework, including reading and writing storage, manipulating the URL hash, and building navigation patterns.
Accessing the Browser Window
All browser API access in euv starts with the window() function, which returns a Window handle. From this handle, you can access the document, location, and various storage mechanisms:
let win: Window = window().expect("no global window exists");
let doc: Document = win.document().expect("should have a document");
The window() function returns an Option<Window>, so you should always handle the case where it might be None (for example, when running in a non-browser environment like server-side rendering). In practice, for WASM applications running in the browser, this will always succeed.
Once you have the Window handle, you can access the Document for DOM manipulation and the Location for navigation.
Working with localStorage
localStorage provides persistent key-value storage that survives browser restarts. euv exposes this through the local_storage() method on Window:
let storage: Option<Storage> = win.local_storage().unwrap_or_default();
if let Some(storage) = storage {
let _ = storage.set_item("key", "value");
let value: Option<String> = storage.get_item("key").unwrap_or_default();
}
Let's break down what's happening:
-
win.local_storage()returns anOption<Storage>. If the browser supports localStorage (which all modern browsers do), this returnsSome(storage). -
set_item("key", "value")writes a value to storage. It returns aResult, which you can handle withlet _ =if you want to ignore errors. -
get_item("key")reads a value from storage, returningOption<String>. The.unwrap_or_default()convertsNone(key not found) to an empty string.
Storing Complex Data
Since localStorage only stores strings, you'll need to serialize and deserialize complex data. euv's JavaScript interop capabilities make it possible to work with JSON:
let storage: Option<Storage> = win.local_storage().unwrap_or_default();
if let Some(storage) = storage {
let _ = storage.set_item("user_name", "Alice");
let name: Option<String> = storage.get_item("user_name").unwrap_or_default();
}
For more complex scenarios, you can use spawn_local with JsFuture to work with JavaScript's JSON.stringify and JSON.parse for serialization.
Practical Example: Persisting User Preferences
A common use case is persisting user preferences across sessions:
let win: Window = window().expect("no global window exists");
let storage: Option<Storage> = win.local_storage().unwrap_or_default();
if let Some(storage) = storage {
let _ = storage.set_item("theme", "dark");
let theme: String = storage.get_item("theme").unwrap_or_default();
}
You could combine this with watch! to automatically save preferences whenever a reactive signal changes, ensuring the stored state is always up to date.
Working with the Location Object
The Location object provides access to the current URL and methods for navigation:
let location: Location = win.location();
let hash: String = location.hash().unwrap_or_default();
let _ = location.set_hash("#/about");
Reading the Current Hash
location.hash() returns the current URL hash (the part after #), which is commonly used for client-side routing in single-page applications:
let location: Location = win.location();
let hash: String = location.hash().unwrap_or_default();
You can use this hash value to determine which view or component should be displayed, enabling client-side routing without page reloads.
Setting the Hash for Navigation
location.set_hash() changes the URL hash, which triggers the browser's hash change event:
let _ = location.set_hash("#/about");
This updates the URL to include #/about without reloading the page. Combined with Conditional-Rendering-and-Patterns, you can build a complete client-side router:
let location: Location = win.location();
let hash: String = location.hash().unwrap_or_default();
// Use the hash to determine which page to render
Building Hash-Based Routing
By combining location.hash() with reactive signals and watch!, you can build a simple but effective routing system:
let location: Location = win.location();
let hash: String = location.hash().unwrap_or_default();
watch!(hash, |h| {
// React to hash changes and update the displayed page
});
Combining Storage and Navigation
Storage and navigation often work together. For example, you might want to save the current page state to storage so that when a user returns to your application, they're taken to the same page:
let win: Window = window().expect("no global window exists");
let location: Location = win.location();
let hash: String = location.hash().unwrap_or_default();
let storage: Option<Storage> = win.local_storage().unwrap_or_default();
if let Some(storage) = storage {
let _ = storage.set_item("last_page", &hash);
let last_page: String = storage.get_item("last_page").unwrap_or_default();
}
This pattern provides a better user experience by restoring their previous state.
Using the Document Object
Beyond storage and location, the Document object gives you access to the full DOM:
let win: Window = window().expect("no global window exists");
let doc: Document = win.document().expect("should have a document");
You can use the Document to create elements, query selectors, and perform other DOM operations as needed.
Error Handling Considerations
Browser APIs can fail for various reasons — storage might be full, the browser might restrict access, or the environment might not support certain features. Always handle Option and Result types appropriately:
let storage: Option<Storage> = win.local_storage().unwrap_or_default();
if let Some(storage) = storage {
let _ = storage.set_item("key", "value");
let value: Option<String> = storage.get_item("key").unwrap_or_default();
}
Using if let Some(...) for storage and .unwrap_or_default() for individual values ensures your application degrades gracefully when browser APIs are unavailable.
Conclusion
euv provides straightforward access to browser storage and navigation APIs through the window() function and its associated objects. By leveraging localStorage for persistent data, Location for URL-based navigation, and Document for DOM access, you can build fully-featured web applications that persist user data and support client-side routing. These APIs integrate naturally with euv's reactive system — you can use watch! to respond to hash changes, store reactive signal values to localStorage, and create rich, state-aware navigation experiences.
Project Code:https://github.com/euv-dev/euv
Top comments (0)