Vanilla JS
Learn how to integrate Fragno fragments into your JavaScript frontend
Vanilla JavaScript support is included in the @fragno-dev/core
package.
To start, create a client instance and call the Vanilla JavaScript useFragno
function.
import { createExampleFragmentClient } from "@fragno-dev/example-fragment";
import { useFragno } from "@fragno-dev/core/vanilla";
const exampleFragmentClient = createExampleFragmentClient();
export const exampleFragment = useFragno(exampleFragmentClient);
Basic Usage
Use the subscribe
method to listen for changes to the store. This method will call your callback
immediately with the current value, then again for every future change.
import { exampleFragment } from "@/lib/example-fragment-client";
exampleFragment.useData().subscribe(({ data, loading }) => {
const dataDisplay = document.getElementById("data-display")!;
dataDisplay.textContent = loading ? "loading..." : data;
});
Understanding Store Methods
Fragno stores provide three different ways to observe data changes: listen
, subscribe
, and the
async iterator. Each serves a different purpose:
subscribe
- Immediate + Future Updates
The subscribe
method calls your callback immediately with the current value, then calls it
again for every future change. This is the most common method for reactive UI updates.
const store = exampleFragment.useData();
// This will be called immediately with current data, then on every change
const unsubscribe = store.subscribe(({ data, loading, error }) => {
console.log("Current state:", { data, loading, error });
updateUI({ data, loading, error });
});
// Unsubscribe when done listening
unsubscribe();
listen
- Future Updates Only
The listen
method only calls your callback for future changes, not the current value. Use this
when you only want to react to updates, not the initial state.
const store = exampleFragment.useData();
// This will only be called for future changes, not the current value
const unsubscribe = store.listen(({ data, loading, error }) => {
console.log("State changed:", { data, loading, error });
// Handle the change...
});
unsubscribe();
Async Iterator - Stream Processing
The async iterator allows you to process data changes as a stream using for await
loops. This is
useful for complex data processing or when you need to wait for specific conditions.
const store = exampleFragment.useData();
// Process each state change as it happens
for await (const { data, loading, error } of store) {
if (loading) {
console.log("Loading...");
} else if (error) {
console.error("Error:", error);
} else {
console.log("Data received:", data);
// Process the data...
}
}
Getting Current State
You can also get the current state synchronously without subscribing:
const store = exampleFragment.useData();
const currentState = store.get();
console.log("Current state:", currentState);
Complex Scenarios
Todo App Example
import { exampleFragment } from "@/lib/example-fragment-client";
// Access the stores
const todosStore = exampleFragment.useTodos();
const addTodoStore = exampleFragment.useAddTodo();
// Subscribe to todos data - this will update UI immediately and on changes
todosStore.subscribe(({ data: todos, loading, error }) => {
if (loading) {
document.getElementById("loading").style.display = "block";
} else {
document.getElementById("loading").style.display = "none";
if (error) {
document.getElementById("error").textContent = error.message;
} else {
renderTodos(todos);
}
}
});
// Listen only to changes (not initial state) for analytics
todosStore.listen(({ data: todos }) => {
// Track when todos change for analytics
analytics.track("todos_updated", { count: todos?.length || 0 });
});
// Trigger actions
document.getElementById("add-todo").addEventListener("click", async () => {
await addTodoStore.mutate({ body: { text: "New todo" } });
});
function renderTodos(todos) {
const container = document.getElementById("todos");
container.innerHTML =
todos?.map((todo) => `<div>${todo.text} - ${todo.done ? "Done" : "Pending"}</div>`).join("") ||
"";
}