diff --git a/src/components/blocking/ChannelSubscriptionScreen.tsx b/src/components/blocking/ChannelSubscriptionScreen.tsx
index d7cb93f..67bb643 100644
--- a/src/components/blocking/ChannelSubscriptionScreen.tsx
+++ b/src/components/blocking/ChannelSubscriptionScreen.tsx
@@ -7,7 +7,8 @@ const CHECK_COOLDOWN_SECONDS = 5;
export default function ChannelSubscriptionScreen() {
const { t } = useTranslation();
- const { channelInfo, clearBlocking } = useBlockingStore();
+ const channelInfo = useBlockingStore((state) => state.channelInfo);
+ const clearBlocking = useBlockingStore((state) => state.clearBlocking);
const [isChecking, setIsChecking] = useState(false);
const [cooldown, setCooldown] = useState(0);
const [error, setError] = useState
(null);
diff --git a/src/components/blocking/MaintenanceScreen.tsx b/src/components/blocking/MaintenanceScreen.tsx
index f51e68c..6302eed 100644
--- a/src/components/blocking/MaintenanceScreen.tsx
+++ b/src/components/blocking/MaintenanceScreen.tsx
@@ -3,7 +3,7 @@ import { useBlockingStore } from '../../store/blocking';
export default function MaintenanceScreen() {
const { t } = useTranslation();
- const { maintenanceInfo } = useBlockingStore();
+ const maintenanceInfo = useBlockingStore((state) => state.maintenanceInfo);
return (
diff --git a/src/components/layout/AppShell/AppHeader.tsx b/src/components/layout/AppShell/AppHeader.tsx
index 1ff846d..f47fe3c 100644
--- a/src/components/layout/AppShell/AppHeader.tsx
+++ b/src/components/layout/AppShell/AppHeader.tsx
@@ -5,6 +5,7 @@ import { useState, useEffect } from 'react';
import { initDataUser } from '@telegram-apps/sdk-react';
import { useAuthStore } from '@/store/auth';
+import { useShallow } from 'zustand/shallow';
import { useTheme } from '@/hooks/useTheme';
import { usePlatform } from '@/platform';
import {
@@ -77,7 +78,9 @@ export function AppHeader({
}: AppHeaderProps) {
const { t } = useTranslation();
const location = useLocation();
- const { user, logout, isAdmin } = useAuthStore();
+ const { user, logout, isAdmin } = useAuthStore(
+ useShallow((state) => ({ user: state.user, logout: state.logout, isAdmin: state.isAdmin })),
+ );
const { toggleTheme, isDark } = useTheme();
const { haptic, platform } = usePlatform();
const [userPhotoUrl, setUserPhotoUrl] = useState(null);
diff --git a/src/components/layout/AppShell/AppShell.tsx b/src/components/layout/AppShell/AppShell.tsx
index eb339f4..0b2de7b 100644
--- a/src/components/layout/AppShell/AppShell.tsx
+++ b/src/components/layout/AppShell/AppShell.tsx
@@ -193,7 +193,8 @@ interface AppShellProps {
export function AppShell({ children }: AppShellProps) {
const { t } = useTranslation();
const location = useLocation();
- const { isAdmin, logout } = useAuthStore();
+ const isAdmin = useAuthStore((state) => state.isAdmin);
+ const logout = useAuthStore((state) => state.logout);
const { isFullscreen, safeAreaInset, contentSafeAreaInset, platform, isMobile } =
useTelegramSDK();
const haptic = useHaptic();
diff --git a/src/components/layout/AppShell/DesktopSidebar.tsx b/src/components/layout/AppShell/DesktopSidebar.tsx
index 8f36328..4af5611 100644
--- a/src/components/layout/AppShell/DesktopSidebar.tsx
+++ b/src/components/layout/AppShell/DesktopSidebar.tsx
@@ -49,7 +49,8 @@ export function DesktopSidebar({
}: DesktopSidebarProps) {
const { t } = useTranslation();
const location = useLocation();
- const { user, logout } = useAuthStore();
+ const user = useAuthStore((state) => state.user);
+ const logout = useAuthStore((state) => state.logout);
const { haptic } = usePlatform();
// Branding
diff --git a/src/components/navigation/CommandPalette/CommandPalette.tsx b/src/components/navigation/CommandPalette/CommandPalette.tsx
index c949ec7..e5add68 100644
--- a/src/components/navigation/CommandPalette/CommandPalette.tsx
+++ b/src/components/navigation/CommandPalette/CommandPalette.tsx
@@ -62,7 +62,7 @@ export function CommandPalette({
const { t } = useTranslation();
const navigate = useNavigate();
const { haptic } = usePlatform();
- const { isAdmin } = useAuthStore();
+ const isAdmin = useAuthStore((state) => state.isAdmin);
const { toggleTheme, isDark } = useTheme();
const [search, setSearch] = useState('');
diff --git a/src/hooks/useFeatureFlags.ts b/src/hooks/useFeatureFlags.ts
index 594e4a9..356d357 100644
--- a/src/hooks/useFeatureFlags.ts
+++ b/src/hooks/useFeatureFlags.ts
@@ -6,7 +6,7 @@ import { contestsApi } from '@/api/contests';
import { pollsApi } from '@/api/polls';
export function useFeatureFlags() {
- const { isAuthenticated } = useAuthStore();
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const { data: referralTerms } = useQuery({
queryKey: ['referral-terms'],
diff --git a/src/pages/Balance.tsx b/src/pages/Balance.tsx
index 0fe2a71..f6f9809 100644
--- a/src/pages/Balance.tsx
+++ b/src/pages/Balance.tsx
@@ -40,7 +40,7 @@ const WalletIcon = ({ className = 'h-8 w-8' }: { className?: string }) => (
export default function Balance() {
const { t } = useTranslation();
- const { refreshUser } = useAuthStore();
+ const refreshUser = useAuthStore((state) => state.refreshUser);
const queryClient = useQueryClient();
const { formatAmount, currencySymbol } = useCurrency();
const [searchParams] = useSearchParams();
diff --git a/src/pages/Connection.tsx b/src/pages/Connection.tsx
index e9fe6c9..2b250cb 100644
--- a/src/pages/Connection.tsx
+++ b/src/pages/Connection.tsx
@@ -14,7 +14,8 @@ import InstallationGuide from '../components/connection/InstallationGuide';
export default function Connection() {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
- const { user, isAdmin } = useAuthStore();
+ const user = useAuthStore((state) => state.user);
+ const isAdmin = useAuthStore((state) => state.isAdmin);
const { isTelegramWebApp } = useTelegramSDK();
const { impact: hapticImpact } = useHaptic();
diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx
index ee246f2..cbccda3 100644
--- a/src/pages/Dashboard.tsx
+++ b/src/pages/Dashboard.tsx
@@ -48,14 +48,15 @@ const RefreshIcon = ({ className = 'w-4 h-4' }: { className?: string }) => (
export default function Dashboard() {
const { t } = useTranslation();
- const { user, refreshUser } = useAuthStore();
+ const user = useAuthStore((state) => state.user);
+ const refreshUser = useAuthStore((state) => state.refreshUser);
const queryClient = useQueryClient();
const navigate = useNavigate();
const { formatAmount, currencySymbol, formatPositive } = useCurrency();
const [trialError, setTrialError] = useState(null);
const { isCompleted: isOnboardingCompleted, complete: completeOnboarding } = useOnboarding();
const [showOnboarding, setShowOnboarding] = useState(false);
- const { blockingType } = useBlockingStore();
+ const blockingType = useBlockingStore((state) => state.blockingType);
// Refresh user data on mount
useEffect(() => {
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
index d429cdb..0203b7e 100644
--- a/src/pages/Login.tsx
+++ b/src/pages/Login.tsx
@@ -3,6 +3,7 @@ import { useNavigate, useLocation } from 'react-router';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { useAuthStore } from '../store/auth';
+import { useShallow } from 'zustand/shallow';
import { authApi } from '../api/auth';
import { isValidEmail } from '../utils/validation';
import {
@@ -32,7 +33,15 @@ export default function Login() {
loginWithTelegram,
loginWithEmail,
registerWithEmail,
- } = useAuthStore();
+ } = useAuthStore(
+ useShallow((state) => ({
+ isAuthenticated: state.isAuthenticated,
+ isLoading: state.isLoading,
+ loginWithTelegram: state.loginWithTelegram,
+ loginWithEmail: state.loginWithEmail,
+ registerWithEmail: state.registerWithEmail,
+ })),
+ );
// Get referral code from localStorage (captured from ?ref= param at module level in auth store)
const referralCode = getPendingReferralCode() || '';
diff --git a/src/pages/OAuthCallback.tsx b/src/pages/OAuthCallback.tsx
index 7078fc5..dfe797f 100644
--- a/src/pages/OAuthCallback.tsx
+++ b/src/pages/OAuthCallback.tsx
@@ -26,7 +26,8 @@ export default function OAuthCallback() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [error, setError] = useState('');
- const { loginWithOAuth, isAuthenticated } = useAuthStore();
+ const loginWithOAuth = useAuthStore((state) => state.loginWithOAuth);
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
useEffect(() => {
if (isAuthenticated) {
diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx
index 1437b26..8884393 100644
--- a/src/pages/Profile.tsx
+++ b/src/pages/Profile.tsx
@@ -61,7 +61,8 @@ const PencilIcon = () => (
export default function Profile() {
const { t } = useTranslation();
- const { user, setUser } = useAuthStore();
+ const user = useAuthStore((state) => state.user);
+ const setUser = useAuthStore((state) => state.setUser);
const queryClient = useQueryClient();
const [email, setEmail] = useState('');
diff --git a/src/pages/TelegramCallback.tsx b/src/pages/TelegramCallback.tsx
index 3908d54..7df21d8 100644
--- a/src/pages/TelegramCallback.tsx
+++ b/src/pages/TelegramCallback.tsx
@@ -8,7 +8,8 @@ export default function TelegramCallback() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [error, setError] = useState('');
- const { loginWithTelegramWidget, isAuthenticated } = useAuthStore();
+ const loginWithTelegramWidget = useAuthStore((state) => state.loginWithTelegramWidget);
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
useEffect(() => {
if (isAuthenticated) {
diff --git a/src/pages/TelegramRedirect.tsx b/src/pages/TelegramRedirect.tsx
index 963b9eb..107a8ef 100644
--- a/src/pages/TelegramRedirect.tsx
+++ b/src/pages/TelegramRedirect.tsx
@@ -3,6 +3,7 @@ import { useNavigate, useSearchParams } from 'react-router';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { useAuthStore } from '../store/auth';
+import { useShallow } from 'zustand/shallow';
import { brandingApi } from '../api/branding';
import { isInTelegramWebApp, getTelegramInitData } from '../hooks/useTelegramSDK';
@@ -33,7 +34,17 @@ export default function TelegramRedirect() {
const { t } = useTranslation();
const navigate = useNavigate();
const [searchParams] = useSearchParams();
- const { loginWithTelegram, isAuthenticated, isLoading: authLoading } = useAuthStore();
+ const {
+ loginWithTelegram,
+ isAuthenticated,
+ isLoading: authLoading,
+ } = useAuthStore(
+ useShallow((state) => ({
+ loginWithTelegram: state.loginWithTelegram,
+ isAuthenticated: state.isAuthenticated,
+ isLoading: state.isLoading,
+ })),
+ );
const [status, setStatus] = useState<'loading' | 'success' | 'error' | 'not-telegram'>('loading');
const [errorMessage, setErrorMessage] = useState('');
const [retryCount, setRetryCount] = useState(() => {
diff --git a/src/pages/VerifyEmail.tsx b/src/pages/VerifyEmail.tsx
index 9f35539..e9f0a6c 100644
--- a/src/pages/VerifyEmail.tsx
+++ b/src/pages/VerifyEmail.tsx
@@ -3,6 +3,7 @@ import { useSearchParams, Link, useNavigate } from 'react-router';
import { useTranslation } from 'react-i18next';
import { authApi } from '../api/auth';
import { useAuthStore } from '../store/auth';
+import { useShallow } from 'zustand/shallow';
import { consumeCampaignSlug } from '../utils/campaign';
import { tokenStorage } from '../utils/token';
import LanguageSwitcher from '../components/LanguageSwitcher';
@@ -13,7 +14,13 @@ export default function VerifyEmail() {
const [searchParams] = useSearchParams();
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
const [error, setError] = useState('');
- const { setTokens, setUser, checkAdminStatus } = useAuthStore();
+ const { setTokens, setUser, checkAdminStatus } = useAuthStore(
+ useShallow((state) => ({
+ setTokens: state.setTokens,
+ setUser: state.setUser,
+ checkAdminStatus: state.checkAdminStatus,
+ })),
+ );
const hasVerified = useRef(false);
useEffect(() => {
diff --git a/src/providers/WebSocketProvider.tsx b/src/providers/WebSocketProvider.tsx
index e4d3445..dd95624 100644
--- a/src/providers/WebSocketProvider.tsx
+++ b/src/providers/WebSocketProvider.tsx
@@ -9,7 +9,8 @@ export type { WSMessage } from './WebSocketContext';
const isDev = import.meta.env.DEV;
export function WebSocketProvider({ children }: { children: React.ReactNode }) {
- const { accessToken, isAuthenticated } = useAuthStore();
+ const accessToken = useAuthStore((state) => state.accessToken);
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const wsRef = useRef(null);
const reconnectTimeoutRef = useRef | null>(null);
const pingIntervalRef = useRef | null>(null);