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

# Getting started

> Install Whop Elements and mount your first element in React, vanilla JavaScript, or Swift.

<Info>**Upcoming.** These docs cover unreleased development, ahead of any published release. Use the channel picker at the top of the sidebar for the docs of a published release.</Info>

<Tabs>
  <Tab title="Web">
    Whop Elements are hosted, themeable UI components you embed in your own site. Each element renders in an isolated frame served from Whop's CDN. You install a thin, fully typed package and the element code stays up to date on its own.

    ## Install

    <CodeGroup>
      ```bash React theme={null}
      npm install @whop/elements-react @whop/elements
      ```

      ```bash Vanilla JS theme={null}
      npm install @whop/elements
      ```
    </CodeGroup>

    ## Mount your first element

    <CodeGroup>
      ```tsx React theme={null}
      import { WhopElements, Payments, PaymentElement, AddressElement, CardElement, EmailElement } from "@whop/elements-react";
      import { loadWhop } from "@whop/elements";

      function Example() {
        return (
          <WhopElements elements={loadWhop()}>
            <Payments /* options */>
              <PaymentElement />
              <AddressElement />
              <CardElement />
              <EmailElement />
            </Payments>
          </WhopElements>
        );
      }
      ```

      ```html Vanilla JS theme={null}
      <script src="https://js.whop.cloud/elements/amber/elements.js" data-whop-elements></script>
      <script type="module">
        const payments = window.WhopElements().payments.create({ /* options */ });
        payments.create('payment').mount('#payments-payment');
        payments.create('address').mount('#payments-address');
        payments.create('card').mount('#payments-card');
        payments.create('email').mount('#payments-email');
      </script>
      ```
    </CodeGroup>

    Prefer npm over the script tag? `loadWhop()` injects the same hosted script and resolves the global constructor:

    ```ts theme={null}
    import { loadWhop } from "@whop/elements";

    const whop = (await loadWhop())({ locale: "en" });
    const payments = whop.payments.create({ /* options */ });
    ```

    ## Global configuration

    Everything you pass at construction applies to every element group created from that instance (a handle's own options can override per group):

    <ResponseField name="appearance" type="Appearance">
      Visual customization for every element — `theme` (light/dark + palettes), `variables` (CSS custom properties), and `classes` (per-part style declarations). The color scheme is applied before an element's first paint, so dark pages never flash light. See [Appearance](/elements/upcoming/appearance).
    </ResponseField>

    <ResponseField name="locale" type="&#x22;en&#x22; | &#x22;es&#x22; | &#x22;zh&#x22; | &#x22;nl&#x22; | &#x22;pt&#x22; | &#x22;de&#x22; | &#x22;it&#x22; | &#x22;fr&#x22; | &#x22;ja&#x22; | &#x22;pl&#x22; | &#x22;tr&#x22;">
      Locale for element UI text — one of the app's built locales; any other value falls back to the default locale. Defaults to `"en"`.
    </ResponseField>

    <ResponseField name="environment" type="&#x22;production&#x22; | &#x22;sandbox&#x22;">
      Which Whop API environment the elements talk to — `"sandbox"` targets the sandbox API (test data; no real money moves). Choosing an environment is the only way to change where the elements send what a buyer types, and both environments are Whop's own. The sandbox environment is not yet generally available. Defaults to `"production"`.
    </ResponseField>

    ```ts theme={null}
    const whop = WhopElements({
      locale: 'es',
      appearance: { theme: { appearance: 'dark', accentColor: 'blue' } }
    });
    ```

    In React, the same object rides `<WhopElements appearance={…} locale={…}>`.

    ## Next

    * [Appearance](/elements/upcoming/appearance): theming, CSS variables, and per-part restyling
    * [Payments](/elements/upcoming/payments/overview): Drives a payment collection surface against the Whop Payments API. Charge config is a plan id OR the inline currency/amount — a plan resolves client-side to the same shape, one path either way. Mount ONE of its faces — the payment element (the full method list), the fused card element, or the exploded card-fields unit — then `payments.createConfirmationToken({ billingDetails })` is the ONE confirm verb for all of them: it tokenizes the selection and mints a confirmation token your server confirms with a secret key; the payment's client\_secret then drives any pending step via `handleNextAction`.
    * [Checkout](/elements/upcoming/checkout/overview): Drives a full hosted checkout for one plan — price summary, promo codes, the currency the buyer pays in, and the whole payment collection surface (the payments elements, composed inside) — against the Whop checkout sessions API. Mount it with a `plan` and the element creates the checkout session itself; the session credential never leaves the element. Or resume a session you already hold by passing its `session` secret. The buyer pays inside the element; you get `onCompleted` with the payment id. A payment needing an off-site step (3DS, a bank page) is driven automatically — on whop.com the buyer is brought back into the restored checkout, and when you embed the element they return to the `returnUrl` you set. EVERY option here is CREATE-TIME: a checkout session is minted at mount from these values, so changing one later (`update()`, or new React props) refuses loudly instead of silently charging the booted order — mount a fresh checkout to change what is being bought.
    * [Ads](/elements/upcoming/ads/overview): An advertising account. Scope it to a company with `accountId` — the ad account underneath is assigned server-side and never surfaces here — then mount `reporting` for what the account spent and what came back, or `campaign-creator` to build a campaign.
    * [Wallet](/elements/upcoming/wallet/overview): Drives an account's money surfaces. `deposit` returns live funding rails; `send` moves money to a recipient or creates a public claim link; `withdraw` collects a payout request; `balances` holds two faces — the holdings list, and the balance block drawing value over a window; `cards` lists issued cards; and `activity` lists ledger movements. Every surface but `deposit` can use the viewer's session when no token is provided.
    * [Websites](/elements/upcoming/websites/overview): An account's websites: every site built on whop.app plus every domain the Whop Pixel reports, with traffic per domain. Mount `websites` and it lists them with visitors, page views, trend, and most-viewed pages. Reading stats is privileged, so it needs an `accessToken` — except inside Whop's own app, where the viewer's session carries the read.
  </Tab>

  <Tab title="Swift">
    Whop Elements are themeable UI components you can import in your iOS app. They ship as a single Swift SPM package. Each one is a plain SwiftUI view that fetches its own data and manages its own state, so a balance screen is a couple of views in a `VStack` instead of a networking layer and a view model. Add the package, tell the SDK how to fetch an access token, and put the elements in your view hierarchy.

    ## Install

    Add the package to your `Package.swift` (or **File → Add Package Dependencies** in Xcode):

    ```swift theme={null}
    dependencies: [
        .package(url: "https://github.com/whopio/whopsdk-elements-swift.git", from: "0.1.12")
    ]
    ```

    ## Requirements

    |           |                       |
    | --------- | --------------------- |
    | Platform  | iOS 18.0+             |
    | Toolchain | Swift 6.0             |
    | Install   | Swift Package Manager |

    ## Before you start

    You need two things:

    1. **The account the elements read.** A company's `biz_…` tag.
    2. **A backend endpoint that mints an access token** for it. Never ship a Whop API key in the app; the key mints tokens, so anyone who extracts it can read the account. Mint server-side and return only the short-lived token.

    Your backend calls [Create Access Token](https://docs.whop.com/api-reference/access-tokens/create-access-token) with the account and the scope the elements need:

    ```http theme={null}
    POST /api/v1/access_tokens
    { "company_id": "biz_xxxxxxxx", "scoped_actions": ["company:balance:read"] }
    ```

    ## Mount your first element

    A complete app. `configure` runs once at launch, and the elements render a spinner until it lands, so nothing races it:

    ```swift theme={null}
    import SwiftUI
    import WhopElements

    /// The SDK calls this again before the token expires, so fetch rather than cache.
    final class WalletTokenProvider: WhopTokenProvider {
        func getToken() async -> WhopTokenResponse {
            WhopTokenResponse(accessToken: await myBackend.whopAccessToken())
        }
    }

    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup {
                WalletScreen()
                    .task { await WhopSDK.configure(tokenProvider: WalletTokenProvider()) }
            }
        }
    }

    struct WalletScreen: View {
        var body: some View {
            ScrollView {
                VStack(spacing: 24) {
                    BalanceElement(accountId: "biz_xxxxxxxx")
                    ListElement(accountId: "biz_xxxxxxxx")
                }
                .padding()
            }
        }
    }
    ```

    ## Authentication

    `getToken()` is your only integration point, and the SDK calls it **again before the token expires**, so return a fresh token each time rather than caching one:

    ```swift theme={null}
    final class WalletTokenProvider: WhopTokenProvider {
        func getToken() async -> WhopTokenResponse {
            // your backend, not the Whop API directly
            WhopTokenResponse(accessToken: await myBackend.whopAccessToken())
        }
    }
    ```

    Configure once, anywhere above the elements. `WhopSDK` is process-wide, so a second call is only needed when the account changes:

    ```swift theme={null}
    await WhopSDK.configure(tokenProvider: WalletTokenProvider())
    ```

    Chat and DMs authenticate the **viewer** instead of an account, through OAuth: `WhopSDK.configureWithOAuth(appId:)`.

    ## Theming

    `.whopTheme(_:)` propagates through the SwiftUI environment, so one call themes every element beneath it. Six roles, each a `WhopTint`:

    ```swift theme={null}
    ListElement(accountId: "biz_xxxxxxxx")
        .whopTheme(WhopTheme(accent: .blue, danger: .red, success: .green))
    ```

    Defaults are `accent: .blue`, `neutral: .gray`, `danger: .red`, `info: .sky`, `success: .green`, `warning: .amber`. `WhopTheme.default` is all six. Views follow the system light/dark appearance on their own.

    ## What the elements handle, and what you own

    Each view fetches its own data and renders its own loading, empty and error states, including a retry. You own **navigation**: the selection callbacks hand you a value and never push a screen.

    ```swift theme={null}
    ListElement(accountId: account) { balance in
        path.append(Route.asset(balance.symbol))   // your router, your screen
    }
    ```

    ## Troubleshooting

    | What you see                 | Why                                                                                                                                                                                                                       |
    | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `Authentication failed`      | The token is missing, expired, or is an **API key** rather than an access token. `getToken()` must return a token minted by [Create Access Token](https://docs.whop.com/api-reference/access-tokens/create-access-token). |
    | A view stays empty           | The token is valid but not scoped for that read. Balances need `company:balance:read`.                                                                                                                                    |
    | `Couldn't load this account` | The `accountId` is not one this token can read.                                                                                                                                                                           |
    | A chart with no line         | Balance history is not available for personal (`user_…`) accounts yet, company accounts only.                                                                                                                             |

    ## Available on iOS

    The wallet views, each with a live simulator on its page:

    * [`BalanceElement`](/elements/upcoming/wallet/balances-balance#swift): the total balance, its trend, and the range picker
    * [`ListElement`](/elements/upcoming/wallet/balances-list#swift): the holdings list
    * [`ActivityElement`](/elements/upcoming/wallet/activity#swift): the ledger activity feed

    `WhopChatView` and `WhopDMsListView` ship in the same package for viewer-authenticated chat. See the [README](https://github.com/whopio/whopsdk-elements-swift).
  </Tab>
</Tabs>
