kriasoft/react-starter-kit

Implement Jotai atoms for global state management

Open

#2093 opened on Aug 6, 2025

View on GitHub
 (0 comments) (0 reactions) (0 assignees)TypeScript (22,485 stars) (4,142 forks)batch import
good first issuehelp wanted

Description

📋 Overview

We need to implement a structured global state management system using Jotai atoms. This will help centralize and organize our application state in a type-safe, reactive way. The goal is to create domain-specific atom files that will manage different aspects of our application state.

🎯 Objective

Create a well-organized atoms directory structure with domain-specific atom files for managing global application state using Jotai's atomic state management pattern.

📁 Directory Structure to Create

apps/web/atoms/
├── user.ts        # Authentication and user state
├── theme.ts       # UI preferences and theming
├── settings.ts    # Application configuration
└── index.ts       # Clean re-exports

✅ Tasks

1. Create apps/web/atoms/user.ts

Implement atoms for user authentication and profile state:

import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

// Primitive atoms (base state)
export const userAtom = atom<User | null>(null);
export const isAuthenticatedAtom = atom<boolean>(false);
export const authTokenAtom = atomWithStorage<string | null>('authToken', null);

// Derived atoms (computed values)
export const userDisplayNameAtom = atom(
  (get) => get(userAtom)?.displayName || get(userAtom)?.email || 'Guest'
);

export const userInitialsAtom = atom((get) => {
  const user = get(userAtom);
  if (\!user) return 'G';
  
  const name = user.displayName || user.email;
  return name
    .split(' ')
    .map(n => n[0])
    .join('')
    .toUpperCase()
    .slice(0, 2);
});

2. Create apps/web/atoms/theme.ts

Implement atoms for UI preferences and theming:

import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

type Theme = 'light' | 'dark' | 'system';
type ColorScheme = 'blue' | 'green' | 'purple' | 'orange';

// Primitive atoms with localStorage persistence
export const themeAtom = atomWithStorage<Theme>('theme', 'system');
export const colorSchemeAtom = atomWithStorage<ColorScheme>('colorScheme', 'blue');
export const sidebarCollapsedAtom = atomWithStorage<boolean>('sidebarCollapsed', false);

// Derived atoms
export const resolvedThemeAtom = atom((get) => {
  const theme = get(themeAtom);
  if (theme \!== 'system') return theme;
  
  // Check system preference
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
});

export const themeClassAtom = atom((get) => {
  const theme = get(resolvedThemeAtom);
  const color = get(colorSchemeAtom);
  return `theme-${theme} color-${color}`;
});

3. Create apps/web/atoms/settings.ts

Implement atoms for application configuration:

import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

interface AppSettings {
  notifications: {
    email: boolean;
    push: boolean;
    sound: boolean;
  };
  privacy: {
    shareAnalytics: boolean;
    showOnlineStatus: boolean;
  };
  locale: string;
  timezone: string;
}

// Default settings
const defaultSettings: AppSettings = {
  notifications: {
    email: true,
    push: true,
    sound: false,
  },
  privacy: {
    shareAnalytics: false,
    showOnlineStatus: true,
  },
  locale: 'en-US',
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
};

// Primitive atom with localStorage persistence
export const settingsAtom = atomWithStorage<AppSettings>('appSettings', defaultSettings);

// Derived atoms for specific settings
export const notificationsEnabledAtom = atom(
  (get) => {
    const settings = get(settingsAtom);
    return settings.notifications.email || settings.notifications.push;
  }
);

export const localeAtom = atom(
  (get) => get(settingsAtom).locale,
  (get, set, locale: string) => {
    const settings = get(settingsAtom);
    set(settingsAtom, { ...settings, locale });
  }
);

4. Create apps/web/atoms/index.ts

Create clean re-exports for all atoms:

// User atoms
export {
  userAtom,
  isAuthenticatedAtom,
  authTokenAtom,
  userDisplayNameAtom,
  userInitialsAtom,
} from './user';

// Theme atoms
export {
  themeAtom,
  colorSchemeAtom,
  sidebarCollapsedAtom,
  resolvedThemeAtom,
  themeClassAtom,
} from './theme';

// Settings atoms
export {
  settingsAtom,
  notificationsEnabledAtom,
  localeAtom,
} from './settings';

💡 Implementation Tips

  1. Use TypeScript: Define proper types for all atom values
  2. Leverage atomWithStorage: Use this for values that should persist across sessions
  3. Create derived atoms: Use read-only atoms for computed values
  4. Keep atoms focused: Each atom should represent a single piece of state
  5. Use atom families: For dynamic collections of similar atoms (if needed)

📚 Example Usage in Components

import { useAtom, useAtomValue } from 'jotai';
import { userAtom, themeAtom, userDisplayNameAtom } from '@/atoms';

function UserProfile() {
  const [user, setUser] = useAtom(userAtom);
  const displayName = useAtomValue(userDisplayNameAtom);
  const [theme, setTheme] = useAtom(themeAtom);
  
  return (
    <div>
      <h1>Welcome, {displayName}\!</h1>
      <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
        Toggle Theme
      </button>
    </div>
  );
}

🧪 Testing Requirements

  • Write unit tests for derived atoms logic
  • Test localStorage persistence for atomWithStorage
  • Verify atom updates trigger component re-renders
  • Test edge cases (null users, invalid settings, etc.)

📖 Resources

⚡ Priority

High - This is foundational work that will improve state management across the entire application.

🎓 Good First Issue Notes

This is an excellent first issue because:

  • Clear, well-defined scope
  • Good introduction to our codebase structure
  • Learn modern React state management patterns
  • No complex business logic required
  • Can be implemented incrementally

❓ Questions?

Feel free to ask questions in the comments! We're here to help you succeed with this contribution.

🔍 Definition of Done

  • All atom files created with proper TypeScript types
  • Atoms follow Jotai best practices
  • Clean re-exports in index.ts
  • Code follows project conventions (functional, modern TypeScript)
  • Basic unit tests for derived atoms
  • No ESLint warnings or TypeScript errors

Contributor guide