App State Management
Save and retrieve persistent data in your gapp.so apps. Use gapp.state to store user preferences, session data, and shared state.
Download for Your AI Tool
Download this guide and paste it into Google AI Studio, Lovable, Cursor, Claude, ChatGPT, or any AI coding tool to let them use gapp.state in your app.
Download Guide (.md)Quick Start
// Save a value (auto-selects user or session scope)
await gapp.state.set('theme', 'dark');
await gapp.state.set('preferences', { sound: true, volume: 80 });
// Get a value
const theme = await gapp.state.get('theme');
console.log(theme); // 'dark'
// Use global state for shared data
await gapp.state.global.set('highScore', 9999);
const highScore = await gapp.state.global.get('highScore');User Identity API
Get information about the currently logged-in user with gapp.user.
gapp.user.isAuthenticated()Check if the current user is logged in.
if (gapp.user.isAuthenticated()) { ... }gapp.user.getName()Get the user's display name.
const name = gapp.user.getName(); // "John Doe"
gapp.user.getUsername()Get the user's @username.
const username = gapp.user.getUsername(); // "johndoe"
gapp.user.getId()Get the user's unique ID.
const id = gapp.user.getId();
gapp.user.getAvatarUrl()Get the user's profile picture URL.
const avatar = gapp.user.getAvatarUrl();
gapp.user.getProfile()Get all user info at once. Returns null if not authenticated.
const profile = gapp.user.getProfile(); // { id, name, username, avatarUrl }Auto-Scope Methods
These methods automatically choose the right scope: user (if logged in) or session (if anonymous).
gapp.state.get(key)Get a value. Uses user scope if authenticated, session scope otherwise.
const theme = await gapp.state.get('theme');gapp.state.set(key, value)Set a value. Automatically routes to user or session scope.
await gapp.state.set('theme', 'dark');gapp.state.delete(key)Delete a value from the current scope.
await gapp.state.delete('theme');gapp.state.list()List all keys in the current scope.
const { keys, quota } = await gapp.state.list();Explicit Scope Methods
For more control, use explicit scope prefixes.
User Scope
Per-user state. Requires authentication. Follows the user across devices.
gapp.state.user.get(key)gapp.state.user.set(key, value)gapp.state.user.delete(key)gapp.state.user.list()Session Scope
Per-device state. Works for anonymous users. Bound to a device ID.
gapp.state.session.get(key)gapp.state.session.set(key, value)gapp.state.session.delete(key)gapp.state.session.list()Global Scope
Shared across all users. Great for leaderboards, counters, shared settings.
gapp.state.global.get(key)gapp.state.global.set(key, value)gapp.state.global.delete(key)gapp.state.global.list()Utility Methods
gapp.state.isAuthenticated()Check if the current user is logged in.
if (gapp.state.isAuthenticated()) { ... }gapp.state.migrateSessionToUser()Move session state to user state after login.
await gapp.state.migrateSessionToUser();
Limits & Quotas
Each scope has storage limits to ensure fair usage.
| Scope | Max Size | Max Keys |
|---|---|---|
| User | 100KB | 100 |
| Session | 100KB | 100 |
| Global | 1MB | 100 |
Key Format
Keys must be 1-100 characters using only letters, numbers, underscores, and hyphens.
// Valid keys
'theme'
'user_preferences'
'high-score-2024'
// Invalid keys
'my key' // no spaces
'key.with.dots' // no dots
'' // not emptyMigration: Session to User
When an anonymous user logs in, you can migrate their session data to their user account.
// After user logs in
if (gapp.state.isAuthenticated()) {
const result = await gapp.state.migrateSessionToUser();
console.log('Migrated keys:', result.migratedKeys);
}Local Development
When developing with Google AI Studio, Cursor, or other tools, the SDK isn't available. Use fallback patterns to handle both environments. See the downloadable guide for complete examples.
// Check if SDK is available
const isGappAvailable = () =>
typeof window !== 'undefined' && window.gapp?.state;
// Storage helper with localStorage fallback
const Storage = {
async get(key) {
if (isGappAvailable()) {
return await gapp.state.get(key);
}
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
},
async set(key, value) {
if (isGappAvailable()) {
return await gapp.state.set(key, value);
}
localStorage.setItem(key, JSON.stringify(value));
}
};
// User helper with anonymous name fallback
const User = {
getName() {
if (isGappAvailable() && gapp.user.isAuthenticated()) {
return gapp.user.getName();
}
return 'Player' + Math.floor(Math.random() * 9999);
}
};