Stores Registry
Manage reactive state using nanostores.
Overview
Stores provide reactive state management with automatic subscriptions. Uizy integrates nanostores for lightweight, framework-agnostic state.
// Register stores
uizy.start({
stores: {
user: {
name: uizy.store.atom("Guest"),
isLoggedIn: uizy.store.atom(false),
},
settings: {
theme: uizy.store.atom("light"),
language: uizy.store.atom("en"),
},
},
});
// Read values
uizy.$("user.name"); // "Guest"
uizy.$("settings.theme"); // "light"
// Update values
uizy.$("user.name", { raw: true }).set("John");
uizy.$("settings.theme", { raw: true }).set("dark");
Store Types
Uizy exposes nanostores via uizy.store:
Atom
Simple value store:
const count = uizy.store.atom(0);
count.get(); // 0
count.set(5); // Sets to 5
count.set((n) => n + 1); // Increment
Map
Object store with key-level updates:
const user = uizy.store.map({
name: "John",
email: "john@example.com",
});
user.get(); // { name: "John", email: "john@example.com" }
user.setKey("name", "Jane");
Computed
Derived values:
const firstName = uizy.store.atom("John");
const lastName = uizy.store.atom("Doe");
const fullName = uizy.store.computed(
[firstName, lastName],
(first, last) => `${first} ${last}`,
);
fullName.get(); // "John Doe"
Registration
Via start()
uizy.start({
stores: {
app: {
isLoading: uizy.store.atom(false),
error: uizy.store.atom(null),
},
user: {
profile: uizy.store.map({
id: null,
name: "",
email: "",
}),
preferences: uizy.store.map({
theme: "system",
notifications: true,
}),
},
},
});
Via uizy.state()
uizy.state({
cart: {
items: uizy.store.atom([]),
total: uizy.store.atom(0),
},
});
Accessing Stores
Get Value
uizy.$("user.name"); // Returns current value
uizy.$("settings.theme"); // "light"
Get Raw Store
Access the store object to call .set(), .subscribe(), etc:
const nameStore = uizy.$("user.name", { raw: true });
nameStore.set("Jane");
nameStore.subscribe((value) => console.log("Name changed:", value));
Silent Mode
uizy.$("nonexistent.path", { silent: true }); // undefined
Shorthand Methods
Uizy provides shorthand methods for common store operations:
uizy.$set(path, value)
Set a store value directly:
// Instead of:
uizy.$("user.name", { raw: true }).set("Jane");
// Use:
uizy.$set("user.name", "Jane");
uizy.$key(path, key, value)
Update a single key in a map store:
// Instead of:
uizy.$("user.profile", { raw: true }).setKey("age", 30);
// Use:
uizy.$key("user.profile", "age", 30);
uizy.$sub(path, callback)
Subscribe to store changes (immediate + updates). Callback is called immediately with current value, then on every change:
// Subscribe to changes
const unsubscribe = uizy.$sub("user.name", (value) => {
console.log("Name:", value); // Called immediately, then on changes
});
// Later: stop listening
unsubscribe();
uizy.$on(path, callback)
Listen to store changes (updates only). Callback is only called when value changes, not immediately:
// Listen for changes only
const unlisten = uizy.$on("user.name", (value) => {
console.log("Name changed to:", value); // NOT called immediately
});
// Later: stop listening
unlisten();
uizy.$computed(aliases, fn)
Create a computed store from multiple registered stores using aliases:
// Register stores first
uizy.state({
project: { name: uizy.store.atom("My App") },
date: { now: uizy.store.atom("2024-01-15") },
});
// Create a computed store with aliases
const $summary = uizy.$computed(
{
myAlias: "project.name",
today: "date.now",
},
({ myAlias, today }) => `${myAlias} live on ${today}`,
);
$summary.get(); // "My App live on 2024-01-15"
// Computed store updates automatically when dependencies change
uizy.$set("project.name", "New App");
$summary.get(); // "New App live on 2024-01-15"
API Reference
uizy.$(path, options?)
Get store value or raw store.
| Parameter | Type | Description |
|---|---|---|
path | string | Dot-notation path |
options.raw | boolean | Return raw store instead of value |
options.silent | boolean | Return undefined if not found |
uizy.$set(path, value)
Set a store value directly.
| Parameter | Type | Description |
|---|---|---|
path | string | Dot-notation path to store |
value | any | Value to set |
uizy.$key(path, key, value)
Update a single key in a map store.
| Parameter | Type | Description |
|---|---|---|
path | string | Dot-notation path to map store |
key | string | Key to update |
value | any | Value to set |
uizy.$sub(path, callback)
Subscribe to store changes. Callback is called immediately with current value, then on every change. Returns unsubscribe function.
| Parameter | Type | Description |
|---|---|---|
path | string | Dot-notation path to store |
callback | (value) => void | Called with value |
| Returns | () => void | Unsubscribe function |
uizy.$on(path, callback)
Listen to store changes. Callback is only called when value changes (not immediately). Returns unlisten function.
| Parameter | Type | Description |
|---|---|---|
path | string | Dot-notation path to store |
callback | (value) => void | Called on changes |
| Returns | () => void | Unlisten function |
uizy.$computed(aliases, fn)
Create a computed store from multiple stores using aliases. Returns a nanostore computed store.
| Parameter | Type | Description |
|---|---|---|
aliases | object | Object mapping alias names to store paths |
fn | (values) => T | Function receiving aliased values, returns computed value |
| Returns | ReadableAtom<T> | A computed store |
uizy.state(items)
Register stores. Merges with existing stores.
uizy.store
Access to nanostores utilities:
| Property | Description |
|---|---|
uizy.store.atom(value) | Create an atom store |
uizy.store.map(object) | Create a map store |
uizy.store.computed(deps, fn) | Create a computed store |
uizy.store.deepMap(object) | Create a deep map store |
Store Methods
Each store has these methods:
| Method | Description |
|---|---|
.get() | Get current value |
.set(value) | Set value |
.subscribe(fn) | Subscribe to changes (immediate + updates) |
.listen(fn) | Listen to changes (updates only) |
Map stores also have:
| Method | Description |
|---|---|
.setKey(key, value) | Update single key |
Examples
User Authentication
uizy.start({
stores: {
auth: {
user: uizy.store.atom(null),
token: uizy.store.atom(null),
isAuthenticated: uizy.store.atom(false),
},
},
actions: {
auth: {
login: async (credentials) => {
const { user, token } = await api.login(credentials);
uizy.$set("auth.user", user);
uizy.$set("auth.token", token);
uizy.$set("auth.isAuthenticated", true);
},
logout: () => {
uizy.$set("auth.user", null);
uizy.$set("auth.token", null);
uizy.$set("auth.isAuthenticated", false);
},
},
},
});
// Check auth status
if (uizy.$("auth.isAuthenticated")) {
console.log("Logged in as:", uizy.$("auth.user").name);
}
Shopping Cart
uizy.state({
cart: {
items: uizy.store.atom([]),
count: uizy.store.atom(0),
total: uizy.store.atom(0),
},
});
uizy.on({
cart: {
add: (product) => {
const current = uizy.$("cart.items");
const updated = [...current, product];
uizy.$set("cart.items", updated);
uizy.$set("cart.count", updated.length);
uizy.$set(
"cart.total",
updated.reduce((sum, p) => sum + p.price, 0),
);
},
remove: (productId) => {
const updated = uizy.$("cart.items").filter((p) => p.id !== productId);
uizy.$set("cart.items", updated);
uizy.$set("cart.count", updated.length);
uizy.$set(
"cart.total",
updated.reduce((sum, p) => sum + p.price, 0),
);
},
clear: () => {
uizy.$set("cart.items", []);
uizy.$set("cart.count", 0);
uizy.$set("cart.total", 0);
},
},
});
Theme Management
uizy.state({
ui: {
theme: uizy.store.atom(localStorage.getItem("theme") || "system"),
sidebarOpen: uizy.store.atom(true),
},
});
// Subscribe to theme changes
uizy.$sub("ui.theme", (theme) => {
document.documentElement.dataset.theme = theme;
localStorage.setItem("theme", theme);
});
// Toggle theme
uizy.on({
ui: {
toggleTheme: () => {
const current = uizy.$("ui.theme");
const next = current === "light" ? "dark" : "light";
uizy.$set("ui.theme", next);
},
},
});
Form State
uizy.state({
form: {
data: uizy.store.map({
name: "",
email: "",
message: "",
}),
errors: uizy.store.map({}),
isSubmitting: uizy.store.atom(false),
},
});
uizy.on({
form: {
setField: ({ field, value }) => {
uizy.$key("form.data", field, value);
},
submit: async () => {
const data = uizy.$("form.data");
uizy.$set("form.isSubmitting", true);
try {
await api.submit(data);
uizy.emit("notification.success", "Form submitted!");
} catch (error) {
uizy.$set("form.errors", error.errors);
} finally {
uizy.$set("form.isSubmitting", false);
}
},
},
});