# JS utilities > Astrol.JSInterop.Base for Blazor: typed localStorage and sessionStorage (with obfuscated values), copy to clipboard, file downloads, time zone, language, media queries and logout on inactivity. Source page: https://doc.astrolui.com/basic-component/js-interop Astrol.JSInterop.Base: browser storage, clipboard, files and browser data from C#. **Astrol.JSInterop.Base** comes with Astrol.Component.Basic and gathers the browser calls almost every app needs, so you don't write JavaScript. Its JS module is downloaded the first time it is used. **AddAstrolBasic()** already registers **AstrolJSInterop**; storage is a set of extension methods of **IJSRuntime**. ```razor @* Program.cs: builder.Services.AddAstrolBasic(); (registers AstrolJSInterop) *@ @using Astrol.JSInterop.Base @inject AstrolJSInterop jsI @* clipboard, files, browser, idle timer *@ @inject IJSRuntime js @* storage: extension methods of IJSRuntime *@ ``` ## 1. localStorage and sessionStorage Store preferences, filters, a wizard's last step or a draft with its C# type: the value becomes JSON when saved and goes back to its type when read; if it doesn't exist you get **default**. **localStorage** survives closing the browser and **sessionStorage** lasts only as long as the tab. By default (**useEncrypt = true**) the value is stored **obfuscated**: it can't be read at a glance in the browser tools, which adds a layer of discretion. It is not security-grade encryption: don't store passwords, tokens or sensitive data in the browser. With **useEncrypt: false** readable JSON is stored (e.g. to share it with JavaScript code). Always read with the same **useEncrypt** value you saved with. Try it: save with and without **useEncrypt** and compare the stored value. ```csharp public record Preferences(string Theme, int PageSize); // Save any serializable value (stored as JSON; obfuscated by default) await js.SetInLocalStorage("prefs", new Preferences("dark", 20)); // Read it back with its type (default if it does not exist) var prefs = await js.GetFromLocalStorage("prefs"); bool exists = await js.ExistsInLocalStorage("prefs"); await js.RemoveFromLocalStorage("prefs"); await js.ClearLocalStorage(); // Same API for sessionStorage (cleared when the tab is closed) await js.SetInSessionStorage("wizard-step", 3); int step = await js.GetFromSessionStorage("wizard-step"); // Plain, readable JSON (e.g. to share it with JavaScript code) await js.SetInLocalStorage("theme", "dark", useEncrypt: false); ``` ## 2. Clipboard and files **CopyToClipboard** copies a text (returns **false** if the browser doesn't allow it). **SaveAs** downloads a file generated in the app from its bytes (a CSV, a PDF, an Excel...) and **DownloadFile** one that is already at a URL, with the name you choose. ```csharp bool copied = await jsI.CopyToClipboard("dotnet add package Astrol.Component.Basic"); // A file generated in the app (CSV, PDF, Excel... as byte[]) await jsI.SaveAs("clients.csv", Encoding.UTF8.GetBytes(csv)); // A file that is already at a URL await jsI.DownloadFile("files/report.pdf", "report-2026.pdf"); ``` ## 3. Browser data and actions The user's time zone and language (for dates and texts), **MatchesMedia** for any CSS media query (mobile, dark mode, print...), opening a new tab safely and scrolling to an element or to the top of the page (also inside the AstrolLayoutMenu body). ```csharp string timeZone = await jsI.GetTimeZone(); // "America/Tijuana" string language = await jsI.GetBrowserLanguage(); // "es-MX" bool isMobile = await jsI.MatchesMedia("(max-width: 768px)"); bool prefersDark = await jsI.MatchesMedia("(prefers-color-scheme: dark)"); await jsI.OpenInNewTab("https://astrolui.com"); await jsI.ScrollToElement("#results"); await jsI.ScrollToTop(); ``` ## 4. Logout on inactivity **IdleTimer** calls your **[JSInvokable] Logout()** method when the user hasn't moved the mouse, typed, scrolled or touched the screen for a while. The last activity is shared between tabs: working in one keeps the session alive in the others. ```csharp // MainLayout.razor.cs: log out after 30 minutes without activity private DotNetObjectReference? self; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { self = DotNetObjectReference.Create(this); await jsI.IdleTimer(self, storageKey: "myapp", time: 30 * 60 * 1000); } } [JSInvokable] public async Task Logout() { await AuthService.LogoutAsync(); Navigation.NavigateTo("/login"); } ``` ### Reference **Storage** (IJSRuntime extensions, **useEncrypt** defaults to **true**) - SetInLocalStorage(key, value, useEncrypt) / SetInSessionStorage(...) - GetFromLocalStorage(key, useEncrypt) / GetFromSessionStorage(...): the value or **default**. - ExistsInLocalStorage(key) / ExistsInSessionStorage(key) - RemoveFromLocalStorage(key) / RemoveFromSessionStorage(key) - ClearLocalStorage() / ClearSessionStorage(): remove every key of the site. **AstrolJSInterop** - CopyToClipboard(text) → **bool** - SaveAs(fileName, bytes) and DownloadFile(url, fileName) - GetTimeZone() and GetBrowserLanguage() → **string**; MatchesMedia(query) → **bool** - OpenInNewTab(url), ScrollToElement(selector, smooth) and ScrollToTop(smooth) - IdleTimer(dotNetRef, storageKey, time): calls **Logout()** after **time** ms without activity (default 30 minutes). - Notification(notification): classic notifications; in new code use **IAstrolToast** (see Notification). - DOM helpers used by the components: FocusElement, GetWidthBySelector, GetHeightBySelector, CountElementExistToHTML, SetInputValue, Prompt.