diff --git a/.changeset/connect-button-explicit-chain-status.md b/.changeset/connect-button-explicit-chain-status.md
new file mode 100644
index 0000000000..f7d2f2e325
--- /dev/null
+++ b/.changeset/connect-button-explicit-chain-status.md
@@ -0,0 +1,17 @@
+---
+"@rainbow-me/rainbowkit": patch
+---
+
+fix: respect explicit `chainStatus` on `ConnectButton` for single-chain dApps
+
+Previously, the chain selector on `ConnectButton` was hidden whenever exactly
+one chain was configured, even when the consumer explicitly set the
+`chainStatus` prop. This made it impossible to keep the active network
+visible on the default button without dropping down to `ConnectButton.Custom`.
+
+The default behavior is unchanged (the selector is still hidden for
+single-chain dApps that do not pass `chainStatus`). Passing any value for
+`chainStatus` (`"full"`, `"icon"`, `"name"`, or `"none"`) is now treated as
+an explicit opt-in: the selector renders for `full`/`icon`/`name`, and stays
+hidden for `none`. This resolves
+[#1418](https://github.com/rainbow-me/rainbowkit/issues/1418).
diff --git a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.test.tsx b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.test.tsx
index 4139014c8c..5905c7bf0d 100644
--- a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.test.tsx
+++ b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.test.tsx
@@ -1,10 +1,12 @@
import { screen, waitFor } from '@testing-library/react';
-import React from 'react';
+import user from '@testing-library/user-event';
+import React, { Fragment } from 'react';
import { describe, expect, it } from 'vitest';
+import { useConnect } from 'wagmi';
import { mainnet } from 'wagmi/chains';
import { renderWithProviders } from '../../../test';
import type { Locale } from '../../locales';
-import { ConnectButton } from './ConnectButton';
+import { ConnectButton, type ConnectButtonProps } from './ConnectButton';
describe('', () => {
const renderTextButton = (locale?: Locale) => {
@@ -41,4 +43,61 @@ describe('', () => {
const button = renderTextButton('ru-RU');
await waitFor(() => expect(button.textContent).toBe('Подключить кошелек'));
});
+
+ describe('chain selector visibility with a single chain', () => {
+ const ConnectAndButton = (props: ConnectButtonProps) => {
+ const { connect, connectors } = useConnect();
+
+ return (
+
+
+
+
+ );
+ };
+
+ const renderConnected = async (props: ConnectButtonProps = {}) => {
+ const result = renderWithProviders(, {
+ chains: [mainnet],
+ });
+
+ const trigger = await result.findByTestId('rk-test-connect');
+ await user.click(trigger);
+
+ return result;
+ };
+
+ it('Hides the chain selector by default when only one chain is configured', async () => {
+ const result = await renderConnected();
+
+ await result.findByTestId('rk-account-button');
+ expect(result.queryByTestId('rk-chain-button')).toBeNull();
+ });
+
+ it('Renders the chain selector when `chainStatus` is set explicitly', async () => {
+ const result = await renderConnected({ chainStatus: 'full' });
+
+ const chainButton = await result.findByTestId('rk-chain-button');
+ expect(chainButton).toBeInTheDocument();
+ });
+
+ it('Hides the chain selector via display sprinkle when `chainStatus="none"` is set explicitly', async () => {
+ const result = await renderConnected({ chainStatus: 'none' });
+
+ await result.findByTestId('rk-account-button');
+ // The chain button is mounted but suppressed via vanilla-extract
+ // `display: none` sprinkles. jsdom does not compute styles from those
+ // CSS modules, so we assert the suppressing class is applied instead
+ // of `toBeVisible()`.
+ const chainButton = result.queryByTestId('rk-chain-button');
+ expect(chainButton).not.toBeNull();
+ expect(chainButton?.className).toMatch(/display_none/);
+ });
+ });
});
diff --git a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx
index 672f788f00..636ed659fe 100644
--- a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx
+++ b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx
@@ -35,7 +35,7 @@ const defaultProps = {
export function ConnectButton({
accountStatus = defaultProps.accountStatus,
- chainStatus = defaultProps.chainStatus,
+ chainStatus: chainStatusProp,
label = defaultProps.label,
showBalance = defaultProps.showBalance,
}: ConnectButtonProps) {
@@ -44,6 +44,13 @@ export function ConnectButton({
const { setShowBalance } = useShowBalance();
const [ready, setReady] = useState(false);
+ // When the consumer explicitly sets `chainStatus`, the chain selector is
+ // rendered even for single-chain dApps so the active network is always
+ // visible (Issue #1418). Without an explicit value we keep the historical
+ // behavior of hiding the selector unless multiple chains are configured.
+ const chainStatusExplicit = chainStatusProp !== undefined;
+ const chainStatus = chainStatusProp ?? defaultProps.chainStatus;
+
const { i18n } = useContext(I18nContext);
// biome-ignore lint/correctness/useExhaustiveDependencies: Preserve existing ready guard behavior.
@@ -80,95 +87,105 @@ export function ConnectButton({
>
{ready && account && connectionStatus === 'connected' ? (
<>
- {chain && (chains.length > 1 || unsupportedChain) && (
-
- value === 'none' ? 'none' : 'flex',
- )}
- fontFamily="body"
- fontWeight="bold"
- gap="6"
- key={
- // Force re-mount to prevent CSS transition
- unsupportedChain ? 'unsupported' : 'supported'
- }
- onClick={openChainModal}
- paddingX="10"
- paddingY="8"
- testId={
- unsupportedChain ? 'wrong-network-button' : 'chain-button'
- }
- transition="default"
- type="button"
- >
- {unsupportedChain ? (
-
- {i18n.t('connect_wallet.wrong_network.label')}
-
- ) : (
-
- {chain.hasIcon ? (
+ {chain &&
+ (chains.length > 1 ||
+ unsupportedChain ||
+ chainStatusExplicit) && (
+
+ value === 'none' ? 'none' : 'flex',
+ )}
+ fontFamily="body"
+ fontWeight="bold"
+ gap="6"
+ key={
+ // Force re-mount to prevent CSS transition
+ unsupportedChain ? 'unsupported' : 'supported'
+ }
+ onClick={openChainModal}
+ paddingX="10"
+ paddingY="8"
+ testId={
+ unsupportedChain
+ ? 'wrong-network-button'
+ : 'chain-button'
+ }
+ transition="default"
+ type="button"
+ >
+ {unsupportedChain ? (
+
+ {i18n.t('connect_wallet.wrong_network.label')}
+
+ ) : (
+
+ {chain.hasIcon ? (
+
+ value === 'full' || value === 'icon'
+ ? 'block'
+ : 'none',
+ )}
+ height="24"
+ width="24"
+ >
+
+
+ ) : null}
- value === 'full' || value === 'icon'
- ? 'block'
- : 'none',
+ display={mapResponsiveValue(
+ chainStatus,
+ (value) => {
+ if (value === 'icon' && !chain.iconUrl) {
+ return 'block'; // Show the chain name if there is no iconUrl
+ }
+
+ return value === 'full' || value === 'name'
+ ? 'block'
+ : 'none';
+ },
)}
- height="24"
- width="24"
>
-
+ {chain.name ?? chain.id}
- ) : null}
- {
- if (value === 'icon' && !chain.iconUrl) {
- return 'block'; // Show the chain name if there is no iconUrl
- }
-
- return value === 'full' || value === 'name'
- ? 'block'
- : 'none';
- })}
- >
- {chain.name ?? chain.id}
-
- )}
-
-
- )}
+ )}
+
+
+ )}
{!unsupportedChain && (
```
+> Note: when only a single chain is configured the chain selector is hidden by
+> default. Pass any `chainStatus` value explicitly (for example `"full"`,
+> `"icon"`, or `"name"`) to force the selector to render so the current
+> network is always visible.
+
#### Show balance
Use the `showBalance` prop to hide/show the balance.