Add to Existing Project

Configure Your Authentication & 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 (if already using Tailwind).
6 // If using Tailwind v4, this will likely be left empty.
7 },
8 {
9 // AccountKit UI theme customizations
10 },
11);

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';

Note: If still using Tailwind v3, skip this step as the tailwind.config.ts file is used by default.

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
({
// Replace with your API key
apiKey: string
apiKey
: "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?: boolean | undefined

If set, calls preparePopupOauth immediately upon initializing the signer. If you intend to use popup-based OAuth login, you must either set this option to true or manually ensure that you call signer.preparePopupOauth() at some point before the user interaction that triggers the OAuth authentication flow.

enablePopupOauth
: true,
// For gas sponsorship (optional) // Learn more here: https://www.alchemy.com/docs/wallets/transactions/sponsor-gas/sponsor-gas-evm
policyId?: string | string[] | undefined
policyId
: "YOUR_POLICY_ID",
}, {
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 your app’s API Key.

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 Smart Wallets config policy in the dashboard.

Note: You can add an "external_wallets" auth method to your config to allow connecting 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" },
}, ];

Initializing Alchemy Provider

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

1. Create a file: providers.tsx

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

2. 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}

3. 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}

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