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.

ScopeMax SizeMax Keys
User100KB100
Session100KB100
Global1MB100

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 empty

Migration: 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);
  }
};