Adding a New State Management Library
Comprehensive guide to integrating additional global state solutions (e.g., MobX, Jotai, Recoil, Valtio) into React Kickstart alongside the existing Redux Toolkit and Zustand options.
Reference Implementations: Study the existing Redux Toolkit and Zustand
implementations in src/features/state-management/
for patterns and
conventions.
๐ฏ Integration Overview
๐ Complete Integration Guide
Declare Dependencies and Versions
File: src/builders/dependencies.js
Add your state management dependencies:
// MobX dependencies
export const mobx = {
mobx: "^6.12.0",
mobxReactLite: "^4.0.7",
mobxStateTree: "^5.4.0" // Optional: for complex state structures
}
export function getMobxDependencies(includeStateTree = false) {
const deps = {
mobx: mobx.mobx,
"mobx-react-lite": mobx.mobxReactLite
}
if (includeStateTree) {
deps["mobx-state-tree"] = mobx.mobxStateTree
}
return deps
}
Wire Dependency Resolution
File: src/builders/dependency-resolver.js
Update state management dependency resolution:
getStateManagementDependencies(stateChoice, framework) {
switch (stateChoice) {
case 'redux':
return getReduxDependencies()
case 'zustand':
return getZustandDependencies()
case 'mobx':
return getMobxDependencies()
case 'jotai':
return getJotaiDependencies()
case 'recoil':
return getRecoilDependencies()
case 'valtio':
return getValtioDependencies()
case 'none':
default:
return {}
}
}
// Handle framework-specific considerations
handleStateFrameworkIntegration(stateChoice, framework) {
const integrations = {}
if (framework === 'nextjs') {
switch (stateChoice) {
case 'mobx':
// MobX needs SSR configuration
integrations.ssrConfig = {
enableStaticRendering: true
}
break
case 'recoil':
// Recoil experimental SSR support
integrations.experimental = true
break
}
}
return integrations
}
Create State Setup Module
File: src/features/state-management/your-state-setup.js
Create comprehensive setup modules:
// src/features/state-management/mobx-setup.js
import { CORE_UTILS } from '../../utils/index.js'
import path from 'path'
export class MobxSetup {
async setup(projectPath, userChoices) {
await this.createStores(projectPath, userChoices)
await this.createRootStore(projectPath, userChoices)
await this.createProvider(projectPath, userChoices)
await this.createExampleComponent(projectPath, userChoices)
await this.handleSSR(projectPath, userChoices)
}
async createStores(projectPath, userChoices) {
const { typescript } = userChoices
// Counter Store
const counterStoreContent = `
import { makeAutoObservable } from 'mobx'
${typescript ? `
export interface CounterState {
count: number
}
` : ''}
class CounterStore${typescript ? ' implements CounterState' : ''} {
count = 0
constructor() {
makeAutoObservable(this)
}
increment = () => {
this.count += 1
}
decrement = () => {
this.count -= 1
}
reset = () => {
this.count = 0
}
incrementBy = (amount${typescript ? ': number' : ''}) => {
this.count += amount
}
// Computed values
get isPositive() {
return this.count > 0
}
get isNegative() {
return this.count < 0
}
get doubleCount() {
return this.count * 2
}
}
export default CounterStore
`
const counterPath = path.join(projectPath, 'src', 'stores', `CounterStore.${typescript ? 'ts' : 'js'}`)
await CORE_UTILS.ensureDirectory(path.dirname(counterPath))
await CORE_UTILS.writeFile(counterPath, counterStoreContent.trim())
// User Store
const userStoreContent = `
import { makeAutoObservable, runInAction } from 'mobx'
${typescript ? `
export interface User {
id: number
name: string
email: string
}
export interface UserState {
users: User[]
currentUser: User | null
loading: boolean
error: string | null
}
` : ''}
class UserStore${typescript ? ' implements UserState' : ''} {
users${typescript ? ': User[]' : ''} = []
currentUser${typescript ? ': User | null' : ''} = null
loading = false
error${typescript ? ': string | null' : ''} = null
constructor() {
makeAutoObservable(this)
}
// Actions
setLoading = (loading${typescript ? ': boolean' : ''}) => {
this.loading = loading
}
setError = (error${typescript ? ': string | null' : ''}) => {
this.error = error
}
setUsers = (users${typescript ? ': User[]' : ''}) => {
this.users = users
}
addUser = (user${typescript ? ': User' : ''}) => {
this.users.push(user)
}
removeUser = (id${typescript ? ': number' : ''}) => {
this.users = this.users.filter(user => user.id !== id)
}
setCurrentUser = (user${typescript ? ': User | null' : ''}) => {
this.currentUser = user
}
// Async actions
fetchUsers = async () => {
this.setLoading(true)
this.setError(null)
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
const users = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
]
runInAction(() => {
this.setUsers(users)
this.setLoading(false)
})
} catch (error) {
runInAction(() => {
this.setError(error.message || 'Failed to fetch users')
this.setLoading(false)
})
}
}
// Computed values
get userCount() {
return this.users.length
}
get hasUsers() {
return this.users.length > 0
}
}
export default UserStore
`
const userPath = path.join(projectPath, 'src', 'stores', `UserStore.${typescript ? 'ts' : 'js'}`)
await CORE_UTILS.writeFile(userPath, userStoreContent.trim())
}
async createRootStore(projectPath, userChoices) {
const { typescript } = userChoices
const rootStoreContent = `
import CounterStore from './CounterStore'
import UserStore from './UserStore'
${typescript ? `
export interface RootStoreType {
counterStore: CounterStore
userStore: UserStore
}
` : ''}
class RootStore${typescript ? ' implements RootStoreType' : ''} {
counterStore${typescript ? ': CounterStore' : ''}
userStore${typescript ? ': UserStore' : ''}
constructor() {
this.counterStore = new CounterStore()
this.userStore = new UserStore()
}
}
export default RootStore
`
const rootPath = path.join(projectPath, 'src', 'stores', `RootStore.${typescript ? 'ts' : 'js'}`)
await CORE_UTILS.writeFile(rootPath, rootStoreContent.trim())
}
async createProvider(projectPath, userChoices) {
const { typescript } = userChoices
const providerContent = `
import React, { createContext, useContext } from 'react'
import RootStore from '../stores/RootStore'
${typescript ? `
interface StoreProviderProps {
children: React.ReactNode
}
` : ''}
const StoreContext = createContext${typescript ? '<RootStore | null>' : ''}(null)
export const StoreProvider${typescript ? ': React.FC<StoreProviderProps>' : ''} = ({ children }) => {
const rootStore = React.useMemo(() => new RootStore(), [])
return (
<StoreContext.Provider value={rootStore}>
{children}
</StoreContext.Provider>
)
}
export const useStore = () => {
const store = useContext(StoreContext)
if (!store) {
throw new Error('useStore must be used within StoreProvider')
}
return store
}
// Individual store hooks for convenience
export const useCounterStore = () => useStore().counterStore
export const useUserStore = () => useStore().userStore
`
const providerPath = path.join(projectPath, 'src', 'contexts', `StoreProvider.${typescript ? 'tsx' : 'jsx'}`)
await CORE_UTILS.ensureDirectory(path.dirname(providerPath))
await CORE_UTILS.writeFile(providerPath, providerContent.trim())
}
async handleSSR(projectPath, userChoices) {
if (userChoices.framework === 'nextjs') {
// Create SSR configuration for MobX
const ssrConfigContent = `
import { enableStaticRendering } from 'mobx-react-lite'
// Enable static rendering for SSR
enableStaticRendering(typeof window === 'undefined')
`
const configPath = path.join(projectPath, 'src', 'lib', 'mobx-ssr.ts')
await CORE_UTILS.ensureDirectory(path.dirname(configPath))
await CORE_UTILS.writeFile(configPath, ssrConfigContent.trim())
}
}
}
Update Prompt System
File: src/prompts/steps/state-management-step.js
Add your state management option:
getChoices(previousAnswers) {
return [
{
name: 'Redux Toolkit (Powerful state management with DevTools)',
value: 'redux',
short: 'Redux'
},
{
name: 'Zustand (Lightweight state management)',
value: 'zustand',
short: 'Zustand'
},
{
name: 'MobX (Reactive state management)',
value: 'mobx',
short: 'MobX'
},
{
name: 'Jotai (Primitive and flexible state management)',
value: 'jotai',
short: 'Jotai'
},
{
name: 'Recoil (Experimental state management by Facebook)',
value: 'recoil',
short: 'Recoil'
},
{
name: 'Valtio (Proxy-based state management)',
value: 'valtio',
short: 'Valtio'
},
{
name: 'None (Use React\'s built-in state)',
value: 'none',
short: 'None'
}
]
}
getRecommendation(previousAnswers) {
// Provide intelligent recommendations
if (previousAnswers.framework === 'nextjs') {
return {
choice: 'zustand',
reason: 'Zustand works excellently with Next.js SSR'
}
}
if (previousAnswers.typescript) {
return {
choice: 'jotai',
reason: 'Jotai has excellent TypeScript support and type inference'
}
}
return {
choice: 'zustand',
reason: 'Lightweight and easy to use for most applications'
}
}
Integrate with Feature System
File: src/features/state-management/index.js
Update the main state management integration:
import { ReduxSetup } from "./redux-setup.js";
import { ZustandSetup } from "./zustand-setup.js";
import { MobxSetup } from "./mobx-setup.js";
import { JotaiSetup } from "./jotai-setup.js";
import { RecoilSetup } from "./recoil-setup.js";
import { ValtioSetup } from "./valtio-setup.js";
export async function setupStateManagement(projectPath, userChoices) {
const { state } = userChoices;
switch (state) {
case "redux":
const reduxSetup = new ReduxSetup();
await reduxSetup.setup(projectPath, userChoices);
break;
case "zustand":
const zustandSetup = new ZustandSetup();
await zustandSetup.setup(projectPath, userChoices);
break;
case "mobx":
const mobxSetup = new MobxSetup();
await mobxSetup.setup(projectPath, userChoices);
break;
case "jotai":
const jotaiSetup = new JotaiSetup();
await jotaiSetup.setup(projectPath, userChoices);
break;
case "recoil":
const recoilSetup = new RecoilSetup();
await recoilSetup.setup(projectPath, userChoices);
break;
case "valtio":
const valtioSetup = new ValtioSetup();
await valtioSetup.setup(projectPath, userChoices);
break;
case "none":
default:
// No state management setup
break;
}
}
export function getStateManagementInfo(userChoices) {
const { state } = userChoices;
const stateInfo = {
mobx: {
description: "Reactive state management through observables",
features: [
"Reactive updates",
"Computed values",
"Actions",
"DevTools support",
],
bundleSize: "~16kb",
learningCurve: "Medium",
},
jotai: {
description: "Primitive and flexible state management",
features: [
"Atomic approach",
"Bottom-up",
"TypeScript first",
"Suspense support",
],
bundleSize: "~3kb",
learningCurve: "Low",
},
recoil: {
description: "Experimental state management by Facebook",
features: [
"Atomic state",
"Derived state",
"Async queries",
"Time travel",
],
bundleSize: "~21kb",
learningCurve: "Medium",
},
valtio: {
description: "Proxy-based state management",
features: [
"Proxy-based",
"Mutable updates",
"Automatic subscriptions",
"Vanilla JS",
],
bundleSize: "~3kb",
learningCurve: "Low",
},
};
return stateInfo[state] || null;
}
Update Template System
File: src/templates/frameworks/vite/content-generator.js
Update templates to integrate state management examples:
generateAppComponent(userChoices) {
const { state, typescript } = userChoices
let imports = [`import React from 'react'`]
let providerWrapper = null
let exampleComponent = 'Counter'
switch (state) {
case 'mobx':
imports.push(`import { StoreProvider } from './contexts/StoreProvider'`)
imports.push(`import { Counter } from './components/Counter'`)
providerWrapper = 'StoreProvider'
break
case 'jotai':
imports.push(`import { JotaiProvider } from './providers/JotaiProvider'`)
imports.push(`import { Counter } from './components/Counter'`)
providerWrapper = 'JotaiProvider'
break
case 'recoil':
imports.push(`import { RecoilRoot } from 'recoil'`)
imports.push(`import { Counter } from './components/Counter'`)
providerWrapper = 'RecoilRoot'
break
case 'valtio':
imports.push(`import { Counter } from './components/Counter'`)
// Valtio doesn't need a provider
break
}
return `${imports.join('\n')}
function App() {
return (
${providerWrapper ? `<${providerWrapper}>` : ''}
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
Welcome to React Kickstart!
</h1>
<p className="text-lg text-gray-600 mb-8">
Your React app with ${state} is ready to go.
</p>
${state !== 'none' ? `<${exampleComponent} />` : ''}
</div>
</div>
${providerWrapper ? `</${providerWrapper}>` : ''}
)
}
export default App`
}
Add to QA System
File: qa-automation/test-matrix-generator.js
Add your state management options to the test matrix:
const CONFIG_OPTIONS = {
framework: ["vite", "nextjs"],
typescript: [true, false],
state: ["redux", "zustand", "mobx", "jotai", "recoil", "valtio", "none"],
// ... other options
};
// Add state-specific validation
const STATE_VALIDATION = {
mobx: {
requiredFiles: [
"src/stores/RootStore.ts",
"src/contexts/StoreProvider.tsx",
],
requiredDependencies: ["mobx", "mobx-react-lite"],
},
jotai: {
requiredFiles: ["src/atoms/index.ts", "src/providers/JotaiProvider.tsx"],
requiredDependencies: ["jotai"],
},
recoil: {
requiredFiles: ["src/recoil/atoms.ts", "src/recoil/selectors.ts"],
requiredDependencies: ["recoil"],
},
valtio: {
requiredFiles: ["src/stores/store.ts"],
requiredDependencies: ["valtio"],
},
};
๐งช Testing Your Integration
Manual Testing
Test your state management option:
# Test with Vite + TypeScript
node bin/react-kickstart.js test-mobx --yes --framework vite --state mobx --typescript
# Test with Next.js
node bin/react-kickstart.js test-jotai --yes --framework nextjs --state jotai
# Test with other features
node bin/react-kickstart.js test-recoil --yes --framework vite --state recoil --api axios --styling tailwind
Integration Testing
Verify the generated project works:
cd test-mobx
npm install
npm run dev # Should start without errors
npm run build # Should build successfully
QA Automation
Run comprehensive tests:
# Generate updated test matrix
node qa-automation/test-matrix-generator.js
# Run critical tests
node qa-automation/test-runner.js critical --full
๐ Best Practices
State Management Design
Consistent Patterns: Follow consistent patterns for state structure, actions, and component integration across all state management solutions.
- Store Structure - Organize state logically with clear separation of concerns
- Action Patterns - Use consistent action naming and structure
- Type Safety - Provide full TypeScript support with proper type definitions
- DevTools Integration - Include development tools and debugging support
- Performance - Consider performance implications and optimization strategies
Framework Compatibility
- SSR Support - Handle server-side rendering requirements for Next.js
- Hydration - Ensure proper hydration for SSR applications
- Bundle Size - Consider the impact on bundle size
- Tree Shaking - Ensure proper tree shaking support
Developer Experience
- Documentation - Provide clear usage examples and patterns
- Error Handling - Include proper error boundaries and error handling
- Testing Utilities - Provide utilities for testing state management
- Migration Guides - Help developers migrate between state solutions
๐ Next Steps
After Integration:
- Test Thoroughly - Test with all framework and feature combinations
- Performance Testing - Benchmark performance with different state sizes
- Documentation - Create comprehensive usage guides and examples
- Community Feedback - Gather feedback from developers using the integration
Related Guides:
- Adding Features โ - General feature addition guide
- Architecture Overview โ - Understanding the system design
- Contributing โ - Contribution guidelines and workflow
State Management Ecosystem: Your state management integration expands React Kickstart's capabilities and provides developers with modern, efficient options for managing application state.