# Quickstart React

Set up Openfort embedded wallets in React. Use the **Ethereum** or **Solana** code snippets in each step according to your chain.

<SkillCard title="AI Skill" subtitle="@openfort/react" source="npm" subtitleHref="https://www.npmjs.com/package/@openfort/react" description="Pre-built prompt with the full setup reference." content={skillContent} fileName="SKILL.md" />

## Install Openfort with the CLI

:::code-group

```sh [pnpm]
pnpm create openfort
```

```sh [npm]
npm create openfort
```

```sh [yarn]
yarn create openfort
```

:::

The **Openfort CLI** helps you set up a new project with all dependencies and configurations. You can select framework (Vite or Next.js), authentication providers, embedded wallet, and UI theming.

:::info
Want a ready-made project? Head to our [recipes section](/docs/recipes).
:::

## Install manually (step by step)

<details>
  <summary style={{ margin: '20px 0 12px 0', fontSize: 16, fontWeight: 300 }}>Starting from scratch? Create a new React app first</summary>

  :::code-group

  ```sh [pnpm]
  pnpm create vite my-openfort-app --template react-ts
  ```

  ```sh [npm]
  npm create vite@latest my-openfort-app -- --template react-ts
  ```

  ```sh [yarn]
  yarn create vite my-openfort-app --template react-ts
  ```

  :::
</details>

::::steps

### Install dependencies

Install [@openfort/react](https://www.npmjs.com/package/@openfort/react) and chain-specific packages. Use the snippet for your chain:

:::code-group

```sh [Ethereum]
pnpm add @openfort/react wagmi @tanstack/react-query viem@^2
```

```sh [Solana]
pnpm add @openfort/react @solana/kit
```

:::

Use `npm install ...` or `yarn add ...` if you prefer those package managers.

### Get your Openfort API keys

In the [API keys](https://dashboard.openfort.io/api-keys) section of the [Openfort dashboard](https://dashboard.openfort.io):

* **Publishable Key**: Safe for client-side use
* **Secret Key**: Server-side only

To generate non-custodial wallets:

1. Scroll to the Shield section and click **Create Shield keys**
2. **Store the encryption share** safely when it appears (you'll only see it once)
3. You'll receive **Shield Publishable Key** and **Shield Secret Key**

### Choose your recovery method

Decide which wallet recovery experience you want to ship:

* **Automatic recovery** — no action from the user required. **Solana uses this by default**; for Ethereum you can choose it and set up a backend endpoint.
* [Passkey recovery](/docs/products/embedded-wallet/react/quickstart/passkey#choose-your-recovery-method) — biometric or device authentication with no backend endpoint.
* [Password recovery](/docs/products/embedded-wallet/react/quickstart/password#choose-your-recovery-method) — user sets their own password with no backend endpoint.

### Set up the recovery endpoint

Required for **Automatic recovery**. Create a backend endpoint that returns a Shield encryption session. See [setting up the recovery endpoint](/docs/products/embedded-wallet/server/automatic-recovery-session).

**One-click deploy:**

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/openfort-xyz/recovery-endpoint-cloudflare)

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/openfort-xyz/recovery-endpoint-vercel\&project-name=shield-recovery-endpoint\&env=SHIELD_PUBLISHABLE_KEY,SHIELD_SECRET_KEY,SHIELD_ENCRYPTION_SHARE\&envDescription=Required%20Shield%20API%20keys%20from%20your%20Openfort%20project\&envLink=https://www.openfort.io/docs)

**Or implement your own:** expose a POST endpoint that returns `{ "session": "<session_id>" }`. See the [backend repo](https://github.com/openfort-xyz/openfort-backend-quickstart) for examples.

Save your endpoint URL for the next step (e.g. `https://your-domain.com/api/protected-create-encryption-session`).

### Set up providers

Wrap your app with the appropriate providers for your chain.

:::info
If using Next.js, add `'use client'` at the top of the file.
:::

## Providers.tsx

:::code-group

```tsx [Ethereum]
import React from "react";
import { AuthProvider, OpenfortProvider, RecoveryMethod } from "@openfort/react";
import { getDefaultConfig, OpenfortWagmiBridge } from "@openfort/react/wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WagmiProvider, createConfig } from "wagmi";
import { baseSepolia } from "viem/chains";

const config = createConfig(
  getDefaultConfig({
    appName: "Openfort Demo App",
    chains: [baseSepolia],
    ssr: true,
  })
);

const queryClient = new QueryClient();

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}> {/* [!code focus] */}
      <WagmiProvider config={config}> {/* [!code focus] */}
        <OpenfortWagmiBridge>  {/* [!code focus] */}
          <OpenfortProvider   {/* [!code focus] */}
            publishableKey="YOUR_OPENFORT_PUBLISHABLE_KEY"
            walletConfig={{
              shieldPublishableKey: "YOUR_OPENFORT_SHIELD_PUBLISHABLE_KEY",
              ethereum: { chainId: 84532 },
              createEncryptedSessionEndpoint: "https://your-domain.com/api/protected-create-encryption-session",
            }}
            uiConfig={{
              authProviders: [AuthProvider.EMAIL_OTP, AuthProvider.GUEST],
              walletRecovery: { defaultMethod: RecoveryMethod.AUTOMATIC },
            }}
          >
            {children}
          </OpenfortProvider> {/* [!code focus] */}
        </OpenfortWagmiBridge> {/* [!code focus] */}
      </WagmiProvider> {/* [!code focus] */}
    </QueryClientProvider> // [!code focus]
  );
}
```

```tsx [Solana]
import React from "react";
import { AuthProvider, ChainTypeEnum, OpenfortProvider, RecoveryMethod } from "@openfort/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      <OpenfortProvider   {/* [!code focus] */}
        publishableKey="YOUR_OPENFORT_PUBLISHABLE_KEY"
        walletConfig={{
          shieldPublishableKey: "YOUR_OPENFORT_SHIELD_PUBLISHABLE_KEY",
          chainType: ChainTypeEnum.SVM, // [!code focus] required for Solana — default is EVM
          solana: { cluster: "devnet" },
          createEncryptedSessionEndpoint: "https://your-domain.com/api/protected-create-encryption-session",
        }}
        uiConfig={{
          authProviders: [AuthProvider.EMAIL_OTP, AuthProvider.GUEST],
          walletRecovery: { defaultMethod: RecoveryMethod.AUTOMATIC },
        }}
      >
        {children}
      </OpenfortProvider> {/* [!code focus] */}
    </QueryClientProvider>
  );
}
```

:::

<details>
  <summary style={{ margin: '20px 0 12px 0', fontSize: 16, fontWeight: 500 }}>Ethereum: Need WalletConnect support?</summary>

  Add `walletConnectProjectId` to `getDefaultConfig`. Get a project ID from the [WalletConnect dashboard](https://cloud.reown.com/sign-in).
</details>

### You're good to go!

Wrap your app in `Providers` and add the Openfort button:

```tsx [App.tsx]
import React from "react";
import { Providers } from "./Providers";
import { OpenfortButton } from "@openfort/react";

export default function App() {
  return (
    <Providers>
      <OpenfortButton />
    </Providers>
  );
}
```

::::

## Advanced provider props

`OpenfortProvider` also accepts these optional props:

| Prop | Type | Description |
|------|------|-------------|
| `overrides` | `SDKOverrides` | Override SDK internals (advanced). |
| `onConnect` | `({ address, connectorId, user }) => void` | Callback fired when a wallet connects. Receives the connected address, connector ID, and user object. |
| `onDisconnect` | `() => void` | Callback fired when a wallet disconnects. |
| `thirdPartyAuth` | `ThirdPartyAuthConfiguration` | Configuration for third-party authentication providers. |
| `walletConfig.connectOnLogin` | `boolean` | Whether to automatically recover/create a wallet after authentication. Set inside `walletConfig`, not as a direct provider prop. Defaults to `true`. |

## Utility exports

`@openfort/react` also exports the following utility functions:

* `createSIWEMessage` — Create a SIWE (Sign-In with Ethereum) message for custom authentication flows.
* `formatAddress` — Format a wallet address for display (truncation, checksumming).
* `getDefaultSolanaRpcUrl` — Get the default Solana RPC URL for a given cluster.
* `invalidateBalance` — Invalidate cached balance data to trigger a refresh.

## Next steps

* [Wallet actions](/docs/products/embedded-wallet/react/wallet/actions)
* [UI Configuration](/docs/products/embedded-wallet/react/ui)
