Persisting Data
Persist widget state across sessions.
The Widget Data API allows you to save state (such as user input, scroll position, or cached API responses) so that it persists across app restarts.
Saving Data
Use setWidgetData from the Core SDK to persist a JSON-serializable object. Cosmo handles the storage.
import { setWidgetData } from "@buildcosmo/widget";
setWidgetData({
lastUpdated: Date.now(),
activeTab: 'settings',
counter: 5
});setWidgetData only accepts plain objects (Record<string, any>). Arrays and primitives are not allowed — wrap them in an object: { items: [...] } or { value: 42 }.
Persistence is asynchronous. Avoid calling setWidgetData in rapid succession (e.g., on every scroll event); use debouncing.
Restoring Data
Cosmo passes the persisted data as the second argument to your widget's entry function.
export default function widget(preferences, widgetData) {
// ...
}export default function widget(preferences, widgetData) {
// ...
}export default function widget(preferences, widgetData) {
// ...
}The widgetData parameter is typed as Record<string, any> | undefined. It contains the exact object that was previously saved using setWidgetData. If no data has been saved yet, it will be undefined.
Example: Persistent Counter
A complete example showing how to initialize state from widgetData and save updates.
import { setWidgetData } from "@buildcosmo/widget";
export default function widget(preferences, widgetData) {
let count = widgetData?.count || 0;
const container = document.getElementById('root')!;
const update = () => {
container.innerHTML = `
<button id="btn">Count: ${count}</button>
`;
document.getElementById('btn')?.addEventListener('click', () => {
count++;
setWidgetData({ count });
update();
});
};
update();
}import { useState } from 'react';
import { setWidgetData } from "@buildcosmo/widget";
export default function Widget({ widgetData }: { widgetData?: Record<string, any> }) {
const [count, setCount] = useState(widgetData?.count || 0);
const increment = () => {
const newCount = count + 1;
setCount(newCount);
setWidgetData({ count: newCount });
};
return <button onClick={increment}>Count: {count}</button>;
}<script setup lang="ts">
import { ref } from 'vue';
import { setWidgetData } from "@buildcosmo/widget";
const props = defineProps<{ widgetData?: Record<string, any> }>();
const count = ref(props.widgetData?.count || 0);
function increment() {
count.value++;
setWidgetData({ count: count.value });
}
</script>
<template>
<button @click="increment">Count: {{ count }}</button>
</template>Best Practices
- Validation: Treat
widgetDataas untrusted input. The schema may differ if you've updated your widget code since the data was saved. - Size Limits: Store only configuration and lightweight state. Avoid large datasets (e.g., base64 images).
- Debouncing: When binding to high-frequency events (input, window resize, scroll), debounce calls to
setWidgetData.