ReactQuickstart

Add to Existing Project

By the end of this tutorial, you’ll have embedded smart wallets with authentication integrated into your existing React application, allowing users to login without seed phrases and perform gasless transactions.

This tutorial assumes you’re using a React application with Next.js app router. If you’re using a different setup, the core concepts remain the same but you may need to adapt the provider setup to your framework.

1. Install the packages

Install the React and Infra packages from Account Kit along with required dependencies.

Prerequisites

  • React 18+
  • TypeScript 5+
$yarn add @account-kit/infra @account-kit/react @tanstack/react-query
>yarn add -D tailwindcss @tailwindcss/postcss postcss

Set up Tailwind CSS

Create a postcss.config.mjs file and add the Tailwind plugin:

postcss.config.mjs
1export default {
2 plugins: {
3 "@tailwindcss/postcss": {},
4 },
5};

Create a global.css file and import it in your app root:

global.css
1@import "tailwindcss";

Using Tailwind v3? You can skip the setup above since you’ve already configured it.

2. Get your API Key

  1. Create an app in the Alchemy Dashboard

    • Make sure your desired network is enabled under the Networks tab
  2. Create a new configuration in your Smart Wallets Dashboard

    • Apply the config to your app from step 1
    Apply your config to the app from the first step
    • Enable the authentication methods you want (email, social login, etc.)
    • For testing, use http://localhost:3000 as your redirect URL
  3. Copy your API key from the dashboard - you’ll need it for the next step

    How to copy the API key

3. Configure your authentication and styling

Create two configuration files that will customize your authentication methods and UI styles.

Create your configuration files

  1. In your project root, create a config.ts file
  2. In your project root, create a tailwind.config.ts file

You can customize these by visiting our demo app - use the interactive sandbox to explore different authentication methods and styling options. When ready, click ‘Code preview’ to export your customized configuration files.

Basic configuration example

Here’s a basic configuration to get you started:

tailwind.config.ts

tailwind.config.ts
1import { withAccountKitUi } from "@account-kit/react/tailwind";
2
3export default withAccountKitUi(
4 {
5 // Your existing tailwind config
6 },
7 {
8 // AccountKit UI theme customizations
9 },
10);

Important: If your tailwind.config.ts already contains any existing config information, be sure to wrap it with withAccountKitUi as shown above. Don’t replace your existing config - just wrap it!

Update your global.css to include the config:

global.css
1@import "tailwindcss";
2@config '../../tailwind.config.ts';

Update your global.css file to include your tailwind.config.ts. (Skip this step if you’re still using Tailwind v3.)

config.ts

import { 
const createConfig: (props: CreateConfigProps, ui?: AlchemyAccountsUIConfig) => AlchemyAccountsConfigWithUI

Wraps the createConfig that is exported from @aa-sdk/core to allow passing an additional argument, the configuration object for the Auth Components UI (the modal and AuthCard).

createConfig
,
const cookieStorage: (config?: { sessionLength?: number; domain?: string; }) => Storage

Function to create cookie based Storage

cookieStorage
} from "@account-kit/react";
import {
class QueryClient
QueryClient
} from "@tanstack/react-query";
import {
const arbitrumSepolia: Chain
arbitrumSepolia
,
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
} from "@account-kit/infra";
export const
const config: AlchemyAccountsConfigWithUI
config
=
function createConfig(props: CreateConfigProps, ui?: AlchemyAccountsUIConfig): AlchemyAccountsConfigWithUI

Wraps the createConfig that is exported from @aa-sdk/core to allow passing an additional argument, the configuration object for the Auth Components UI (the modal and AuthCard).

createConfig
(
{
transport: AlchemyTransport
transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "YOUR_API_KEY" }), // Replace with your API key
chain: Chain
chain
:
const arbitrumSepolia: Chain
arbitrumSepolia
,
ssr?: boolean | undefined

Enable this parameter if you are using the config in an SSR setting (eg. NextJS) Turing this setting on will disable automatic hydration of the client store

ssr
: true,
storage?: CreateStorageFn | undefined
storage
:
const cookieStorage: (config?: { sessionLength?: number; domain?: string; }) => Storage

Function to create cookie based Storage

cookieStorage
,
enablePopupOauth: true
enablePopupOauth
: true,
}, {
auth?: { addPasskeyOnSignup?: boolean; header?: React.ReactNode; hideError?: boolean; onAuthSuccess?: () => void; sections: AuthType[][]; hideSignInText?: boolean; } | undefined
auth
: {
sections: AuthType[][]

Each section can contain multiple auth types which will be grouped together and separated by an OR divider

sections
: [
[{
type: "email"
type
: "email" }],
[ {
type: "passkey"
type
: "passkey" },
{
type: "social"
type
: "social",
authProviderId: KnownAuthProvider
authProviderId
: "google",
mode: "popup"
mode
: "popup" },
], ],
addPasskeyOnSignup?: boolean | undefined

If this is true, then auth components will prompt users to add a passkey after signing in for the first time

addPasskeyOnSignup
: true,
}, }, ); export const
const queryClient: QueryClient
queryClient
= new
new QueryClient(config?: QueryClientConfig): QueryClient
QueryClient
();

Remember to replace "YOUR_API_KEY" with the API key from step 2.

Important: The chain you import (like arbitrumSepolia) must come from the @account-kit/infra package, not from viem. Additionally, make sure this chain is enabled in both your Alchemy app and Account Kit config policy in the dashboard.

Note: This is set up allows you to connect to other existing external EOAs such as MetaMask using our built-in connectors and WalletConnect. Learn more here

[
  {
    
type: string
type
: "external_wallets",
walletConnect: { projectId: string; }
walletConnect
: {
projectId: string
projectId
: "your-project-id" },
}, ];

4. Set up the Alchemy Provider

Wrap your application with the Alchemy Provider to enable embedded wallet functionality.

Create providers.tsx

app/providers.tsx
1"use client";
2import { config, queryClient } from "@/config";
3import { AlchemyClientState } from "@account-kit/core";
4import { AlchemyAccountProvider } from "@account-kit/react";
5import { QueryClientProvider } from "@tanstack/react-query";
6import { PropsWithChildren } from "react";
7
8export const Providers = (
9 props: PropsWithChildren<{ initialState?: AlchemyClientState }>,
10) => {
11 return (
12 <QueryClientProvider client={queryClient}>
13 <AlchemyAccountProvider
14 config={config}
15 queryClient={queryClient}
16 initialState={props.initialState}
17 >
18 {props.children}
19 </AlchemyAccountProvider>
20 </QueryClientProvider>
21 );
22};

Update your layout.tsx

app/layout.tsx
1import { config } from "@/config";
2import { cookieToInitialState } from "@account-kit/core";
3import type { Metadata } from "next";
4import { Inter } from "next/font/google";
5import { headers } from "next/headers";
6import "./globals.css";
7import { Providers } from "./providers";
8
9const inter = Inter({ subsets: ["latin"] });
10
11export const metadata: Metadata = {
12 title: "My App with Embedded Wallets",
13 description: "My app with Alchemy Smart Wallets integration",
14};
15
16export default function RootLayout({
17 children,
18}: Readonly<{
19 children: React.ReactNode;
20}>) {
21 const initialState = cookieToInitialState(
22 config,
23 headers().get("cookie") ?? undefined,
24 );
25
26 return (
27 <html lang="en">
28 <body className={inter.className}>
29 <Providers initialState={initialState}>{children}</Providers>
30 </body>
31 </html>
32 );
33}

5. Add authentication to your app

Now you can use the Alchemy React components to add wallet authentication anywhere in your app.

Example page with login functionality

app/page.tsx
1"use client";
2import {
3 useAuthModal,
4 useLogout,
5 useSignerStatus,
6 useUser,
7} from "@account-kit/react";
8
9export default function Home() {
10 const user = useUser();
11 const { openAuthModal } = useAuthModal();
12 const signerStatus = useSignerStatus();
13 const { logout } = useLogout();
14
15 return (
16 <main className="flex min-h-screen flex-col items-center p-24 gap-4 justify-center text-center">
17 {signerStatus.isInitializing ? (
18 <>Loading...</>
19 ) : user ? (
20 <div className="flex flex-col gap-2 p-2">
21 <p className="text-xl font-bold">Success!</p>
22 You're logged in as {user.email ?? "anon"}.
23 <button
24 className="akui-btn akui-btn-primary mt-6"
25 onClick={() => logout()}
26 >
27 Log out
28 </button>
29 </div>
30 ) : (
31 <button className="akui-btn akui-btn-primary" onClick={openAuthModal}>
32 Login
33 </button>
34 )}
35 </main>
36 );
37}

6. Test your integration

  1. Start your development server
  2. Navigate to your app in the browser
  3. Click the “Login” button
  4. Try authenticating with different methods (email, social login, etc.)
  5. Once authenticated, you should see the success message with the user’s information

Congratulations! You now have embedded smart wallets working in your React application.

What’s next?

Now that you have basic authentication working, you can explore additional features: