Skip to main content
The useUser hook provides functions to manage user identity, attributes, and subscription status in the Superwall SDK.

Basic Usage

import { useUser } from "expo-superwall";

function ProfileScreen() {
  const { identify, user, subscriptionStatus } = useUser();

  return (
    <View>
      <Text>User ID: {user?.appUserId}</Text>
      <Text>Status: {subscriptionStatus?.status}</Text>
    </View>
  );
}

User Identification

Identify users when they log in to track their behavior and purchases:
1

Call identify() on login

const { identify } = useUser();

const handleLogin = async (userId: string) => {
  try {
    await identify(userId);
    console.log("User identified:", userId);
  } catch (error) {
    console.error("Failed to identify user:", error);
  }
};
2

Use consistent user IDs

Use the same ID as your authentication system:
// ✅ Good - consistent with your auth system
await identify(firebaseUser.uid);
await identify(supabaseUser.id);
await identify(clerkUser.id);

// ❌ Bad - random or changing IDs
await identify(`user_${Date.now()}`);
3

Sign out when user logs out

const { signOut } = useUser();

const handleLogout = async () => {
  await signOut();
  // User identity is now reset
};

Identification Options

Restore paywall assignments from previous sessions:
await identify(userId, {
  restorePaywallAssignments: true,
});
When restorePaywallAssignments is true, the SDK attempts to restore the user’s previous paywall variant assignments. This ensures consistent A/B test experiences across sessions.

User Attributes

User attributes help you:
  • Target specific user segments with paywalls
  • Personalize paywall content
  • Track user behavior
  • Make data-driven decisions

Built-in Attributes

The SDK automatically tracks these attributes:
const { user } = useUser();

console.log(user?.appUserId);               // Your app's user ID
console.log(user?.aliasId);                 // Superwall's internal alias
console.log(user?.applicationInstalledAt);  // ISO date of app install
console.log(user?.seed);                    // Seed for variant assignment

Custom Attributes

Set custom attributes to enrich user data:
const { update } = useUser();

// Set attributes directly
await update({
  name: "John Doe",
  email: "[email protected]",
  plan: "free",
  onboardingCompleted: true,
  signupDate: new Date().toISOString(),
});

Updating Attributes with Functions

Use a function to update based on previous values:
const { update } = useUser();

// Increment a counter
await update((oldAttributes) => ({
  ...oldAttributes,
  exportCount: (oldAttributes.exportCount || 0) + 1,
}));

// Update nested properties
await update((oldAttributes) => ({
  ...oldAttributes,
  preferences: {
    ...oldAttributes.preferences,
    theme: "dark",
  },
}));

Practical Examples

const { update } = useUser();

// Track onboarding progress
await update({
  onboardingStep: 3,
  onboardingCompleted: false,
});

// After onboarding
await update({
  onboardingCompleted: true,
  onboardingCompletedAt: new Date().toISOString(),
});

Subscription Status

Track and manage the user’s subscription state:
const { subscriptionStatus } = useUser();

console.log(subscriptionStatus?.status);
// "UNKNOWN" | "INACTIVE" | "ACTIVE"

if (subscriptionStatus?.status === "ACTIVE") {
  console.log("Entitlements:", subscriptionStatus.entitlements);
  // [{ id: "premium", type: "SERVICE_LEVEL" }]
}

Subscription Status Types

// Unknown status (initial state)
{ status: "UNKNOWN" }

// User not subscribed
{ status: "INACTIVE" }

// User has active subscription
{
  status: "ACTIVE",
  entitlements: [
    { id: "premium", type: "SERVICE_LEVEL" },
    { id: "pro_features", type: "SERVICE_LEVEL" },
  ]
}

Setting Subscription Status

Manually set status after purchase or restore:
const { setSubscriptionStatus } = useUser();

// After successful purchase
await setSubscriptionStatus({
  status: "ACTIVE",
  entitlements: [
    { id: "premium", type: "SERVICE_LEVEL" },
  ],
});

// After subscription expires
await setSubscriptionStatus({
  status: "INACTIVE",
});
Only use setSubscriptionStatus if you’re managing subscriptions manually. The SDK automatically tracks subscription status when using native purchase flows.

Entitlements

Retrieve detailed entitlement information:
const { getEntitlements } = useUser();

const entitlements = await getEntitlements();
console.log(entitlements);
// {
//   active: [{ id: "premium", type: "SERVICE_LEVEL" }],
//   inactive: [{ id: "trial", type: "SERVICE_LEVEL" }]
// }

Syncing Subscription Status

Sync subscription status from entitlements:
const { getEntitlements, setSubscriptionStatus } = useUser();

const syncSubscription = async () => {
  const entitlements = await getEntitlements();
  
  if (entitlements.active.length > 0) {
    await setSubscriptionStatus({
      status: "ACTIVE",
      entitlements: entitlements.active,
    });
  } else {
    await setSubscriptionStatus({
      status: "INACTIVE",
    });
  }
};

Refreshing User Data

Manually refresh user attributes and subscription status:
const { refresh } = useUser();

const handleRefresh = async () => {
  try {
    const updatedAttributes = await refresh();
    console.log("User data refreshed:", updatedAttributes);
  } catch (error) {
    console.error("Failed to refresh:", error);
  }
};

Complete Example

Here’s a comprehensive user management implementation:
import { useUser } from "expo-superwall";
import { View, Text, Button, TextInput } from "react-native";
import { useState, useEffect } from "react";

function UserProfileScreen() {
  const {
    identify,
    update,
    signOut,
    refresh,
    user,
    subscriptionStatus,
    setSubscriptionStatus,
    getEntitlements,
  } = useUser();

  const [name, setName] = useState("");

  // Load user data on mount
  useEffect(() => {
    if (user?.name) {
      setName(user.name);
    }
  }, [user]);

  const handleLogin = async (userId: string) => {
    try {
      await identify(userId, {
        restorePaywallAssignments: true,
      });
      
      // Set initial user attributes
      await update({
        loginAt: new Date().toISOString(),
        platform: Platform.OS,
      });
    } catch (error) {
      console.error("Login failed:", error);
    }
  };

  const handleUpdateProfile = async () => {
    await update({
      name,
      profileUpdatedAt: new Date().toISOString(),
    });
  };

  const handleLogout = async () => {
    await signOut();
    setName("");
  };

  const handleRefresh = async () => {
    const updatedData = await refresh();
    console.log("Refreshed:", updatedData);
  };

  const syncSubscriptionStatus = async () => {
    const entitlements = await getEntitlements();
    
    if (entitlements.active.length > 0) {
      await setSubscriptionStatus({
        status: "ACTIVE",
        entitlements: entitlements.active,
      });
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Text style={{ fontSize: 24, marginBottom: 20 }}>User Profile</Text>

      {user ? (
        <View>
          <Text>User ID: {user.appUserId}</Text>
          <Text>Alias: {user.aliasId}</Text>
          <Text>Installed: {new Date(user.applicationInstalledAt).toLocaleDateString()}</Text>
          
          <View style={{ marginTop: 20 }}>
            <Text>Subscription: {subscriptionStatus?.status}</Text>
            {subscriptionStatus?.status === "ACTIVE" && (
              <Text>
                Entitlements: {subscriptionStatus.entitlements.map(e => e.id).join(", ")}
              </Text>
            )}
          </View>

          <TextInput
            value={name}
            onChangeText={setName}
            placeholder="Name"
            style={{ borderWidth: 1, padding: 10, marginTop: 20 }}
          />
          <Button title="Update Profile" onPress={handleUpdateProfile} />

          <View style={{ marginTop: 20 }}>
            <Button title="Refresh Data" onPress={handleRefresh} />
            <Button title="Sync Subscription" onPress={syncSubscriptionStatus} />
            <Button title="Logout" onPress={handleLogout} color="red" />
          </View>
        </View>
      ) : (
        <View>
          <Text>Not logged in</Text>
          <Button
            title="Login"
            onPress={() => handleLogin("user_123")}
          />
        </View>
      )}
    </View>
  );
}

export default UserProfileScreen;

User Attribute Patterns

Track onboarding progress:
await update({
  onboardingStep: currentStep,
  onboardingCompleted: false,
  onboardingStartedAt: new Date().toISOString(),
});

Best Practices

Call identify() as soon as you have a user ID:
useEffect(() => {
  if (authUser?.uid) {
    identify(authUser.uid);
  }
}, [authUser]);
Choose clear, consistent names:
  • onboardingCompleted, lastActiveAt, documentsCreated
  • flag1, temp, x
Update attributes when relevant events occur:
// On document creation
await update((prev) => ({
  ...prev,
  documentsCreated: (prev.documentsCreated || 0) + 1,
}));
Avoid storing:
  • Passwords
  • Credit card numbers
  • Social security numbers
  • Private personal information

Next Steps

Handling Subscriptions

Learn about subscription status and entitlements

Integration Attributes

Link third-party analytics and attribution platforms

Build docs developers (and LLMs) love