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

# React

> `@runonatlas/react` is our React library for Atlas, which provides all the components and hooks you need.

## Installation

<Tabs>
  <Tab title="npm">
    ```bash Terminal theme={null}
    npm install @runonatlas/react
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash Terminal theme={null}
    pnpm add @runonatlas/react
    ```
  </Tab>

  <Tab title="yarn">
    ```bash Terminal theme={null}
    yarn add @runonatlas/react
    ```
  </Tab>
</Tabs>

## Initialization

<Warning>
  This only covers the frontend initialization with React. You will also need to initialize your backend. For example, [Express](/developer-portal/sdk/express).
</Warning>

To initialize your frontend application, you need to create a client provider component and wrap your application with it. Since Atlas needs to authenticate with your backend and verify that users are only accessing what they can, you will need to place it under your authentication provider.

It's very important that you provide all the required props:

* `getAuth`: A function that returns the authentication token. This is only necessary if the authentication with the backend is done with tokens and headers (for example, Clerk).
* `loginCallback`: A function that is called whenever the user tries to do an action that requires authentication.
* `userId`: The ID of the user.
* `userEmail`: The email of the user. Optional.
* `userName`: The name of the user. Optional.
* `isUserLoading`: A boolean that indicates whether the user is still loading. *Optional.* Helps Atlas to avoid flickering when the user status changes.

You then need to wrap your application with the `AtlasProvider`, wherever you have the rest of your providers and under your authentication provider.

For example, with Clerk:

<CodeGroup>
  ```tsx src/atlas/client.tsx expandable theme={null}
  import { useAuth, useClerk, useUser } from "@clerk/clerk-react";
  import { AtlasProvider } from "@runonatlas/react";
  import { useCallback } from "react";

  export function AtlasClientProvider({
    children,
  }: {
    children: React.ReactNode;
  }) {
    const { getToken, userId, isLoaded } = useAuth();
    const { user } = useUser();
    const { openSignIn } = useClerk();

    const loginCallback = useCallback(() => {
      openSignIn();
    }, [openSignIn]);

    return (
      <AtlasProvider
        getAuth={getToken}
        loginCallback={loginCallback}
        userEmail={user?.primaryEmailAddress?.emailAddress}
        userId={userId}
        userName={user?.fullName}
        isUserLoading={!isLoaded}
      >
        {children}
      </AtlasProvider>
    );
  }
  ```

  ```tsx src/App.tsx expandable theme={null}
  import { ClerkProvider } from "@clerk/clerk-react";

  export default function App() {
    return (
      <ClerkProvider>
        <AtlasClientProvider>
          <YourApp />
        </AtlasClientProvider>
      </ClerkProvider>
    );
  }
  ```
</CodeGroup>

<Accordion title="Using a different host for your backend?" icon="server">
  You can specify the host of your backend when instantiating the provider.

  ```tsx src/atlas/client.tsx expandable {22} theme={null}
  import { useAuth, useClerk, useUser } from "@clerk/clerk-react";
  import { AtlasProvider } from "@runonatlas/react";
  import { useCallback } from "react";

  export function AtlasClientProvider({
    children,
  }: {
    children: React.ReactNode;
  }) {
    const { getToken, userId } = useAuth();
    const { user } = useUser();
    const { openSignIn } = useClerk();

    const loginCallback = useCallback(() => {
      openSignIn();
    }, [openSignIn]);

    return (
      <AtlasProvider
        getAuth={getToken}
        loginCallback={loginCallback}
        host="http://localhost:3001"
        userEmail={user?.primaryEmailAddress?.emailAddress}
        userId={userId}
        userName={user?.fullName}
      >
        {children}
      </AtlasProvider>
    );
  }
  ```
</Accordion>

## Use our Atlas widgets

<Warning>
  Remember that you need to have followed the [installation guide](#installation) before you can use any of these widgets.
</Warning>

You can use any of our Atlas widgets in your React application by importing them from `@runonatlas/react`.

### PricingComponent Widget

The PricingComponent widget shows the plans in your Atlas pricing model and allows users to purhcase a subscription to a plan via Atlas' in-line checkout.

**IMPORTANT:** The `successUrl` must be absolute. For example, `/success` is not valid, but `https://your-app.com/success` is valid.

```tsx theme={null}
import { PricingComponent } from "@runonatlas/react";

/**
 * IMPORTANT:
 * Replace the `successUrl` prop with values specific to your application.
 */
export default function PricingPage() {
  return (
    <PricingComponent
      // Where to redirect after successful checkout.
      successUrl={"https://your-app.com/customer-portal"}
    />
  );
}
```

If you want to display your pricing model on a **public facing marketing site**, you can embed a hosted pricing page via `iFrame`. When a visitor selects a plan, they'll be redirected to your core application where the SDK is installed to sign in and complete the purchase.

To embed pricing on a website outside your app, use the `/pricing-embed` endpoint and pass the following URL query parameters:

* `publishableKey` - public key with read access to your pricing model (accessible in [settings/api-keys](https://app.runonatlas.com/settings/api-keys))
* `redirectTo` - absolute URL to redirect users to your app when they select a plan
* `successUrl` - absolute URL to return users to after they succesfully purchase a plan

**IMPORTANT:** The  `redirectTo` and`successUrl` must be absolute. For example, `/success` is not valid, but `https://your-app.com/success` is valid.

```html theme={null}
<iframe
  src="https://app.runonatlas.com/pricing-embed?publishableKey=<YOUR_PUBLISHABLE_KEY>&redirectTo=<YOUR_REDIRECT_URL>&successUrl=<YOUR_SUCCESS_URL>"
  style="width: 100%; height: 650px; border: none;"
  title="Pricing"
  allowFullScreen
></iframe>
```

### CustomerPortalComponent Widget

The CustomerPortalComponent widget shows the current user what plan they are subscribed to, a history of their payments, and allows them to cancel or change their plan at any time.

**IMPORTANT:** The `successUrl` must be absolute. For example, `/success` is not valid, but `https://your-app.com/success` is valid.

```tsx theme={null}
import { CustomerPortalComponent } from "@runonatlas/react";

/**
 * IMPORTANT:
 * Replace the `successUrl` prop with values specific to your application.
 */
export default function CustomerPortalPage() {
  return (
    <CustomerPortalComponent
      // Where to redirect after successful checkout.
      successUrl={"https://your-app.com/customer-portal"}
    />
  );
}
```

## Limit user access based on their subscriptions

You can prevent users from accessing restricted parts of your application based on their subscription. To do so, you can use our UI protection features.

### UI Protection

#### Using hooks

You can use the `useFeaturesAllowed` hook to check if a user has access to a feature.

```tsx theme={null}
import { useFeaturesAllowed } from "@runonatlas/react";

export default function Home() {
  const { isAllowed } = useFeaturesAllowed(["feature-1", "feature-2"]);

  return (
    <div>
      Our system has determined that...
      {isAllowed ? "You have access to this feature!" : "You don't have access to this feature. Please upgrade your subscription!"}
    </div>
  );
}
```

Or you can use the `useCustomerFeatures` hook to get a users access status for all features in your pricing model:

```tsx theme={null}
import { useFeaturesAllowed } from "@runonatlas/react";

...

const { features } = useCustomerFeatures();
console.log(features); // { "data-explorer": { allowed: true, included: true, limit: 10, currentUsage: 5 }, "ai-assistant": { allowed: false, included: false } }
```

#### Using components

You can use the `FeatureProtect` component to check if a user has access to a feature.

```tsx theme={null}
import { FeatureProtect } from "@runonatlas/react";

export default function Home() {
  return (
    <FeatureProtect
      disallowedFallback={<div>Oh, you don't have access to this cool feature. Upgrade now!</div>}
      features={["feature-1", "feature-2"]}
    >
      <div>Cool feature</div>
    </FeatureProtect>
  );
}
```

## Limit-based features

Sometimes, just having a feature as enabled or disabled is not enough, and our pricing models require limits to be set. For example, 5 users per account, or 20GB of storage.
Setting this up with Atlas is very easy. And, if at some point the limits change, you won't need to change the code again!

<Warning>
  Make sure to configure the limits also on your backend. For example, [Express](/sdk/express).
</Warning>

### Explanation in the UI

When using both the `<FeatureProtect>` component and the `useFeaturesAllowed()` hook, it will automatically check if the user has access to the features you are protecting AND if the limit has not been reached.

However, it is possible that you want to show your user why the access was denied! To to do this, we give you the reasons why the access was denied. For example, with the hook `useFeaturesAllowed`:

```tsx src/routes/home/index.tsx expandable theme={null}
import { useFeaturesAllowed } from "@runonatlas/react";

export default function Home() {
  const { isAllowed, reasons } = useFeaturesAllowed(["feature-1", "feature-2"]);

  if (!isAllowed) {
    return (
      <div>
        Our system has determined that...
        {reasons.map((reason) => {
          if (reason.reason === "notIncluded") {
            return (
              <div key={reason.slug}>
                You need to upgrade your plan to access {reason.slug}
              </div>
            );
          }

          if (reason.reason === "limitReached") {
            return (
              <div key={reason.slug}>
                You have reached the limit for {reason.slug}. You currently have {reason.currentUsage}/{reason.limit}.
              </div>
            );
          }
        })}
      </div>
    );
  }

  return <div>Cool feature</div>;
}
```

In the `<FeatureProtect>` component, instead of directly providing the FallbackComponent, you can actually provide a function that returns the component to be rendered when the access is denied. For example:

```tsx src/routes/home/index.tsx expandable theme={null}
import { FeatureProtect } from "@runonatlas/react";

export default function Home() {
  return (
    <FeatureProtect
      disallowedFallback={(reasons) => {
        return (
          <div>
            Our system has determined that...
            {reasons.map((reason) => {
              if (reason.reason === "notIncluded") {
                return (
                  <div key={reason.slug}>
                    You need to upgrade your plan to access {reason.slug}
                  </div>
                );
              }

              if (reason.reason === "limitReached") {
                return (
                  <div key={reason.slug}>
                    You have reached the limit for {reason.slug}. You currently have {reason.currentUsage}/{reason.limit}.
                  </div>
                );
              }
            })}
          </div>
        );
      }}
      features={["feature-1", "feature-2"]}
    >
      <div>Cool feature</div>
    </FeatureProtect>
  );
}
```

## Organizations

<Warning>
  This only covers the frontend configuration. You will also need to configure your backend. For example, [Express](/developer-portal/sdk/express?#organizations).
</Warning>

In many applications, it's common to allow multiple users to belong to the same organization and share a single subscription. For example: Alice creates an organization called **Standard Corp**, purchases a plan, and invites Bob and Carol to join. Since all three are members of the same organization, they should share access to the same plan and limits.

To enable this, you should use the **organization ID** as the user identifier instead of the individual user ID. This tells Atlas that access and usage should be scoped to the organization — not the individual — ensuring that Bob and Carol both have access under Standard Corp’s plan.

<CodeGroup>
  ```tsx src/atlas/client.tsx expandable {1,12,24,25} theme={null}
  import { useAuth, useClerk, useOrganization, useUser } from "@clerk/clerk-react";
  import { AtlasProvider } from "@runonatlas/react";
  import { useCallback } from "react";

  export function AtlasClientProvider({
    children,
  }: {
    children: React.ReactNode;
  }) {
    const { getToken } = useAuth();
    const { user } = useUser();
    const { organization } = useOrganization();
    const { openSignIn } = useClerk();

    const loginCallback = useCallback(() => {
      openSignIn();
    }, [openSignIn]);

    return (
      <AtlasProvider
        getAuth={getToken}
        loginCallback={loginCallback}
        userEmail={user?.primaryEmailAddress?.emailAddress}
        userId={organization?.id}
        userName={organization?.name}
      >
        {children}
      </AtlasProvider>
    );
  }
  ```
</CodeGroup>
