> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrade by popup

> Open the Dynamic upgrade app in a popup window (web) or the system auth session (React Native) without navigating away, using openUpgradePopup.

Open the app in a separate window instead of navigating away or embedding it. The current page never unloads. Reach for this when a full-page redirect is too heavy but you don't want the app inline — and it's the **recommended route for React Native**, where it runs in the system auth session (`ASWebAuthenticationSession` on iOS / Chrome Custom Tabs on Android). See [the overview](/docs/javascript/building-ui/legacy-wallet-upgrade) for detection, hosting, and the shared CORS requirements.

<Warning>
  On the web, browsers block popups that aren't opened from a user gesture. Call `openUpgradePopup` (or the hook's `open`) **synchronously inside the click handler** — don't `await` anything before it, or the popup is blocked and you get an `UpgradePopupBlockedError`.
</Warning>

<Tabs>
  <Tab title="TypeScript">
    `openUpgradePopup` opens the window synchronously and resolves `completed` when the app reports done (refreshing the session on success). `completed` rejects with an `UpgradePopupClosedError` if the user closes the popup first, and the call throws an `UpgradePopupBlockedError` if the browser blocks it.

    ```typescript theme={"system"}
    import {
      openUpgradePopup,
      UpgradePopupClosedError,
    } from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade';

    // On your "Upgrade wallet" button's click handler — no await before this call.
    function startUpgrade() {
      const session = openUpgradePopup();

      session.completed
        .then((outcome) => {
          if (outcome.status === 'success') {
            // Session already refreshed. refreshUi is your implementation.
            refreshUi();
          } else {
            offerRetry();
          }
        })
        .catch((error) => {
          if (error instanceof UpgradePopupClosedError) {
            // The user dismissed the popup before finishing.
            offerRetry();
          }
        });

      // If you need to dismiss it yourself (e.g. the user navigates away):
      // session.close();
    }
    ```
  </Tab>

  <Tab title="React">
    `useUpgradePopup` wraps the popup lifecycle: call `open` from a click handler, then drive your UI off `result` (the outcome once complete, `undefined` until then) and `error` (set on a dismissed/blocked popup or open failure). `onComplete` / `onError` callbacks are also available if you prefer. The popup is closed for you on unmount.

    ```tsx theme={"system"}
    import { useUpgradePopup } from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade/react';

    function UpgradeButton() {
      const { open, result, error } = useUpgradePopup();

      if (result?.status === 'success') {
        // The session was already refreshed for you.
        return <p>Wallet upgraded.</p>;
      }

      // error covers a dismissed popup (UpgradePopupClosedError), a blocked popup
      // (UpgradePopupBlockedError), and open failures; result.status 'error' covers
      // a failed upgrade.
      const failed = error !== undefined || result?.status === 'error';

      return (
        <>
          <button type="button" onClick={open}>
            Upgrade wallet
          </button>
          {failed && <p>Upgrade failed — try again.</p>}
        </>
      );
    }
    ```
  </Tab>

  <Tab title="React Native">
    There is no browser window on React Native, so `useUpgradePopup` runs the app in the **system auth session** the SDK already uses for native OAuth. The app returns to your configured deep link, and the outcome is parsed from the callback URL (refreshing the session on success). This requires the same client configuration native social sign-in needs — `metadata.nativeLink` and `coreConfig.openAuthSession` must be set. The hook exposes the same `result` / `error` values as on web.

    ```tsx theme={"system"}
    import { useUpgradePopup } from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade/react';

    // Requires metadata.nativeLink + coreConfig.openAuthSession on the client.
    function UpgradeButton() {
      const { open, result, error } = useUpgradePopup();

      if (result?.status === 'success') {
        // The session was already refreshed for you.
        return <Text>Wallet upgraded.</Text>;
      }

      // error is set when the user dismisses the auth session; result.status
      // 'error' covers a failed upgrade.
      const failed = error !== undefined || result?.status === 'error';

      return (
        <>
          <Button title="Upgrade wallet" onPress={open} />
          {failed && <Text>Upgrade failed — try again.</Text>}
        </>
      );
    }
    ```

    <Note>
      The `redirectUrl` defaults to `metadata.nativeLink`. Pass an explicit `redirectUrl` only if you want to return to a different registered deep link.
    </Note>
  </Tab>
</Tabs>

## Handling errors

| Situation                                      | What to do                                                                                                   |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Popup rejected with `UpgradePopupBlockedError` | The browser blocked the popup — open it synchronously from a click handler, with no `await` before the call. |
| Popup rejected with `UpgradePopupClosedError`  | The user closed the popup / dismissed the auth session before finishing — offer a retry.                     |
| Outcome `status` is `error`                    | The upgrade failed or was cancelled — offer a retry.                                                         |

## See also

* [Upgrade overview](/docs/javascript/building-ui/legacy-wallet-upgrade) — detection, hosting, and shared requirements
* [Redirect to the app](/docs/javascript/building-ui/legacy-wallet-upgrade/redirect)
* [Embed the app (iframe)](/docs/javascript/building-ui/legacy-wallet-upgrade/embed)
* [Going lower-level](/docs/javascript/building-ui/legacy-wallet-upgrade/lower-level)
