UI Customization
This guide assumes you’re using Next.js, if you’re using a different framework or need more info checkout our full tailwind setup guide
Create two configuration files that will customize your authentication methods and UI styles.
Create your configuration files
- In your project root, create a
config.ts
file - In your project root, create a
tailwind.config.ts
file
Basic configuration example
Start with a basic configuration:
tailwind.config.ts
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:
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) => AlchemyAccountsConfigWithUIWraps 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;
}) => StorageFunction to create cookie based Storage
cookieStorage } from "@account-kit/react";
import { class QueryClientQueryClient } from "@tanstack/react-query";
import { const arbitrumSepolia: ChainarbitrumSepolia, function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: AlchemyAccountsConfigWithUIconfig = function createConfig(props: CreateConfigProps, ui?: AlchemyAccountsUIConfig): AlchemyAccountsConfigWithUIWraps 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: AlchemyTransporttransport: function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: stringapiKey: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnvThe process.env
property returns an object containing the user environment. See environ(7)
.
An example of this object looks like:
jsTERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node'
It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker
threads. In other words, the following example would not work:
bash node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
env.foo = 'bar'; console.log(env.foo); ```
Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean.
```js import env from 'node:process';
env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ```
Use `delete` to delete a property from `process.env`.
```js import env from 'node:process';
env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ```
On Windows operating systems, environment variables are case-insensitive.
```js import env from 'node:process';
env.TEST = 1; console.log(env.test); // => 1 ```
Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
env.string | undefinedNEXT_PUBLIC_ALCHEMY_API_KEY,
}),
chain: Chainchain: const arbitrumSepolia: ChainarbitrumSepolia,
ssr?: boolean | undefinedEnable 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 | undefinedstorage: const cookieStorage: (config?: {
sessionLength?: number;
domain?: string;
}) => StorageFunction to create cookie based Storage
cookieStorage,
enablePopupOauth?: boolean | undefinedIf 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
// Learn more here: https://www.alchemy.com/docs/wallets/transactions/sponsor-gas
policyId?: string | string[] | undefinedpolicyId: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnvThe process.env
property returns an object containing the user environment. See environ(7)
.
An example of this object looks like:
jsTERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node'
It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker
threads. In other words, the following example would not work:
bash node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
env.foo = 'bar'; console.log(env.foo); ```
Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean.
```js import env from 'node:process';
env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ```
Use `delete` to delete a property from `process.env`.
```js import env from 'node:process';
env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ```
On Windows operating systems, environment variables are case-insensitive.
```js import env from 'node:process';
env.TEST = 1; console.log(env.test); // => 1 ```
Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
env.string | undefinedNEXT_PUBLIC_ALCHEMY_POLICY_ID,
},
{
auth?: {
addPasskeyOnSignup?: boolean;
header?: React.ReactNode;
hideError?: boolean;
onAuthSuccess?: () => void;
sections: AuthType[][];
hideSignInText?: boolean;
} | undefinedauth: {
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: KnownAuthProviderauthProviderId: "google", mode: "popup"mode: "popup" },
],
],
addPasskeyOnSignup?: boolean | undefinedIf 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: QueryClientqueryClient = new new QueryClient(config?: QueryClientConfig): QueryClientQueryClient();
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: stringtype: "external_wallets",
walletConnect: {
projectId: string;
}walletConnect: { projectId: stringprojectId: "your-project-id" },
},
];
Customization
Customize both configuration files 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.
You can also follow these links to learn more about using UI Components and theming.
Finally, to the last step: integration into the application.